STM32F103复用JTAG/SWD引脚为GPIO

news2025/4/15 5:10:53

普中-精灵1开发板,主芯片为STM32F103C8T6,4个独立按键K1~K4依次接PA15~PA12,按下为低电平,8个LED灯D1~D8,依次接PA0~PA7。查询手册得知:PA15主功能为JTDI,PA14为JTCK/SWCLK,PA13为JTMS/SWDIO。采用串口进行程序下载(普中ISP)。用4个按键,控制4个LED,主程序如下:

/*按键程序,普中-精灵1开发板*/
#include "stm32f10x.h"
#include "delay.h"
#include "key.h"
int main(void)
{ 
	KEY_Init();
    // 初始化 LED 亮灭标志为 0      0:熄灭    1:亮起
   unsigned int led_flag1 = 0;
   unsigned int led_flag2 = 0;
   unsigned int led_flag3 = 0;
   unsigned int led_flag4 = 0;
    // 初始化 按键按下标志为 0      0:弹起    1:按下
    unsigned int key_is_press1 = 0;
    unsigned int key_is_press2 = 0;
    unsigned int key_is_press3 = 0;
    unsigned int key_is_press4 = 0;
   while (1)
    { 
        if( key_is_press1 == 0)  //无按键
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15) ==0)
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15)==0 )
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press1 = 1;
                    // 设置 LED 亮灭标志翻转一次
                    led_flag1 = ~led_flag1;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press1 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15)==0 )
            {   // 按键仍然处于按下状态,不进行任何操作
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press1 = 0;
            }
					}	
   if( key_is_press2 == 0)
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14) ==0)
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==0 )
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press2 = 1;
                    // 设置 LED 亮灭标志翻转一次
                    led_flag2 = ~led_flag2;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press2 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==0 )
            {   // 按键仍然处于按下状态,不进行任何操作
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press2 = 0;
            }			
						
        }				
   if( key_is_press3 == 0)
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13) ==0)
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13)==0 )
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press3 = 1;
                    // 设置 LED 亮灭标志翻转一次
                    led_flag3 = ~led_flag3;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press3 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13)==0 )
            {   // 按键仍然处于按下状态,不进行任何操作
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press3 = 0;
            }			
        }
 if( key_is_press4 == 0)
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) ==0)
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12)==0 )
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press4 = 1;
                    // 设置 LED 亮灭标志翻转一次
                    led_flag4 = ~led_flag4;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press4 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
            if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12)==0 )
            {   // 按键仍然处于按下状态,不进行任何操作
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press4 = 0;
            }			
        }
        
        // 根据按键按下的标志状态操作功能
        // 如果按键被按下了,执行功能
        if( key_is_press1 == 1)
        {   
	// 根据 LED 的亮灭标志控制 LED 亮起和熄灭
            if(led_flag1)
            {
                // 点亮 LED
                GPIO_ResetBits(GPIOA, GPIO_Pin_0 ); 
            }
            else
            {
                // 熄灭 LED
                GPIO_SetBits(GPIOA, GPIO_Pin_0 ); 
            }               
        }

 if( key_is_press2 == 1)
        {   // 根据 LED 的亮灭标志控制 LED 亮起和熄灭
            if(led_flag2)
            {
                // 点亮 LED
                GPIO_ResetBits(GPIOA, GPIO_Pin_1 ); 
            }
            else
            {
                // 熄灭 LED
               GPIO_SetBits(GPIOA, GPIO_Pin_1 ); 
            }               
        }	
 if( key_is_press3 == 1)
        {   // 根据 LED 的亮灭标志控制 LED 亮起和熄灭
            if(led_flag3)
            {
                // 点亮 LED
               GPIO_ResetBits(GPIOA, GPIO_Pin_2 ); 
            }
            else
            {
                // 熄灭 LED
              GPIO_SetBits(GPIOA, GPIO_Pin_2 ); 
            }               
        }	
			  if( key_is_press4 == 1)
        {   // 根据 LED 的亮灭标志控制 LED 亮起和熄灭
            if(led_flag4)
            {
                // 点亮 LED
                GPIO_ResetBits(GPIOA, GPIO_Pin_3 ); 
            }
            else
            {
                // 熄灭 LED
                GPIO_SetBits(GPIOA, GPIO_Pin_3 ); 
            }               
        }					
				
    }
			
}

按键初始化程序key.c如下:

/*key.c按键初始化
*/
#include "key.h"
#include "delay.h"
void KEY_Init(void)
{ 
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);  // 开启时钟
  //GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;       //配置GPIO频率为50MHz
  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15 ;	
  //GPIO_SetBits(GPIOA, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  GPIO_SetBits(GPIOA, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); 
}

执行以上,会发现K1、K3、K4功能正常,而K2(PA14)无效,此时D2(PA1)常亮,按照手册,若此时禁掉SWD,即去掉GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);前面的注释,此时并无效果,反而有可能出现K1(PA15)、K3(PA13)也失效的情形。此时,需要执行非常关键的一个操作,即对PA12、PA13、PA14、PA15置1,且要在设置MODE之前进行,去掉GPIO_SetBits(GPIOA, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);前面的注释,会发现K2功能也正常了。实际上,如果用CMSIS-DAP(实际为WCH CMSIS-DAP,SW模式,BOOT0=1)下载程序,不需要增加GPIO_SetBits(GPIOA, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);这条指令,K2功能也是正常的。但神奇的是,利用J-Link(实际为J-Link ARM-OB STM32,SW模式,BOOT0=1)、ST-Link(实际为ST-Link V2,SW模式,BOOT0=1),则和ISP方式效果一致。

在普中的这款开发板做进一步开发,例如把按键和数码管结合起来,按K1数码管显示加1,按K2减1,按K3数码管左移1位,按K4数码管右移1位。,主程序如下:

/*按键驱动数码管,普中-精灵1开发板*/
#include "stm32f10x.h"
#include "delay.h"
#include "key.h"
uint16_t table[] = {0xFF3F,0xFF06,0xFF5b,0xFF4f,0xFF66,0xFF6d,0xFF7d,0xFF07,0xFF7f,0xFF6f}; //段码
uint16_t  wei[]={0xff00,0xff20,0xff10,0xff30,0xff08,0xff28,0xff18,0xff38}; //位码
static uint8_t k=0;  //声明变量 k,j
static uint8_t j=0;
int main(void)
{   KEY_Init();
    unsigned int key_is_press1 = 0;
    unsigned int key_is_press2 = 0;
    unsigned int key_is_press3 = 0;
    unsigned int key_is_press4 = 0;
    while (1)
    { 
        if( !key_is_press1)  //无按键
        {
            if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15))
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15) )
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press1 = 1;
			  	    j=j+1;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press1 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
         {
           if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15) ) //按键弹起,设置按键按下标志为 0
           key_is_press1 = 0;
    	 }	
   if( !key_is_press2)
        {
            if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14))
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14))
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press2 = 1;
  					j=j-1;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press2 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
           if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14) ) // 按键弹起,设置按键按下标志为 0
           key_is_press2 = 0;
	        }				
   if( !key_is_press3 )
        {
            if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13))
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13))
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press3 = 1;
				    k++;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press3 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
           if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13) ) // 按键弹起,设置按键按下标志为 0
           key_is_press3 = 0;
	        }
	 if(!key_is_press4 )
        {
            if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12))
            {   // 如果按键按下
                Delay(20); // 按键消抖
                if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) )
                {   // 如果按键被按下,设置按键按下标志为 1
                    key_is_press4 = 1;
		            k--;
                }
            }
            else
            {   // 如果按键弹起,设置按键按下标志为 0
                key_is_press4 = 0;
            }
        }
        else    // 如果按键已经按下了,判断按键是否弹起
        {
          if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) ) // 按键弹起,设置按键按下标志为 0
              key_is_press4 = 0;
		 }
   GPIO_Write(GPIOA,table[j%8] );
   GPIO_Write(GPIOB, wei[k%8] );
	  }
}

段码数组要32位数赋值,因为GPIO_Write(GPIOA,...)默认情况下是对整个PA进行操作,如果只给出16位数,那么高16位数将被赋值为0,显然这样就有可能导致读数据失败。参考按键初始化程序:

/*key.c
按键初始化
*/
#include "key.h"
#include "delay.h"
void KEY_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);  // 开启时钟
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;       //配置GPIO频率为50MHz
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15 ;	
	GPIO_SetBits(GPIOA, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; 
	GPIO_SetBits(GPIOA, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
 	GPIO_Init(GPIOA, &GPIO_InitStructure); 
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[wifi SAE]wpa3-personal

SAE :Simultaneous Authentication of Equals(同等同时认证) wpa2和wpa3之间最大的区别是认证过程的区别 WPA2不安全性 1.sta和ap预置psk(AP密码) 2.四次握手生成ptk用于后续数据加密的密钥 ptk计算基于psk、双方随机数; 双方都产…

电路方案分析(二十)TPS63xxx系列DC/DC电源EMI PCB设计方案

tips:资料来自网络,仅供学习使用。[TOC](TPS63xxx系列DC/DC电源EMI PCB设计方案) 1.概述 通过TPS63xxx系列DC/DC电源模块来分析降低直流/直流降压/升压转换器辐射 EMI 的来源以及相关PCB设计。 下面都以最常用的TPS63070为例说明: 典型应用…

Java 大厂面试题 -- JVM 深度剖析:解锁大厂 Offe 的核心密钥

最近佳作推荐: Java大厂面试高频考点|分布式系统JVM优化实战全解析(附真题)(New) Java大厂面试题 – JVM 优化进阶之路:从原理到实战的深度剖析(2)(New&#…

目标追踪Hyperspectral Adapter for Object Tracking based on Hyperspectral Video

论文作者:Long Gao,Yunhe Zhang,Langkun Chen,Yan Jiang,Weiying Xie,Yunsong Li 作者单位:Xidian University;the University of Sheffield 论文链接:http://arxiv.org/abs/2503.22199v1 内容简介: 1)方向&#x…

深度剖析SSD多段L2P表查找加速技术

在固态硬盘(SSD)控制器中,逻辑块地址(LBA)需要通过映射表(L2P Table)映射到NAND闪存的物理地址(PA)。随着SSD容量的增长,L2P表的大小也随之增加,这给查找操作带来了性能挑战。 在SSD控制器中,LBA需借助L2P表映射为NAND物理地址。映射表最小规模为 (O(n * \lg (n)))…

【MQTT-协议原理】

MQTT-协议原理 ■ MQTT-协议原理■ MQTT-服务器 称为"消息代理"(Broker)■ MQTT协议中的订阅、主题、会话■ 一、订阅(Subscription)■ 二、会话(Session)■ 三、主题名(Topic Name&a…

PCIe 5.0光学SSD原型问世!

近日,Kioxia Corporation(铠侠)、AIO Core Co., Ltd. 和 Kyocera Corporation(京瓷)联合宣布成功开发了一款支持 PCIe 5.0 接口的光学 SSD 原型。该技术旨在通过光接口替换传统的电接口,从而显著增加计算设…

Raymarching Textures In Depth

本节课最主要的就是学会hlsl中使用纹理采样 float4 color Texture2DSample(Texobj, TexobjSampler, uv); return color; 课程中的代码(没有这张图我就没做) 课程代码产生深度的原因是uv偏移,黑色区域会不断向左偏移,直到找到白色…

文献总结:ECCV2022-BEVFormer

BEVFormer 一、文章基本信息二、文章背景三、BEVFormer架构(1) BEV 查询(2) 空间交叉注意力机制(3) 时间自注意力机制(4) BEV应用(5) 实施细节 四、实验五、总结 一、文章基本信息 标题BEVFormer: Learning Bird’s-Eye-view Representation from Multi-camera images via spa…

Openlayers:海量图形渲染之WebGL渲染

最近由于在工作中涉及到了海量图形渲染的问题,因此我开始研究相关的解决方案。我在网络上寻找相关的解决方案时发现许多的文章都提到利用Openlayers中的WebGLPointsLayer类,可以实现渲染海量的点,之后我又了解到利用WebGLVectorLayer类可以渲…

RCE漏洞学习

1,What is RCE? 在CTF(Capture The Flag)竞赛中,RCE漏洞指的是远程代码执行漏洞(Remote Code Execution)。这类漏洞允许攻击者通过某种方式在目标系统上执行任意代码,从而完全控制目…

如何使用 Grafana 连接 Easyearch

Grafana 介绍 Grafana 是一款开源的跨平台数据可视化与监控分析工具,专为时序数据(如服务器性能指标、应用程序日志、业务数据等)设计。它通过直观的仪表盘(Dashboards)帮助用户实时监控系统状态、分析趋势&#xff0…

mindsdb AI 开源的查询引擎 - 用于构建 AI 的平台,该平台可以学习和回答大规模联合数据的问题。

一、软件介绍 文末提供源码和程序下载学习 MindsDB 是一种解决方案,使人类、AI、代理和应用程序能够以自然语言和 SQL 查询数据,并在不同的数据源和类型中获得高度准确的答案。此开源程序是一个联合查询引擎,可以整理您的数据蔓延混乱&#…

BOTA六维力矩传感器如何打通机器人AI力控操作的三层架构?感知-决策-执行全链路揭秘

想象一下,你对着一个机器人说:“请帮我泡杯茶。”然后,它就真的开始行动了:找茶壶、烧水、取茶叶、泡茶……这一切看似简单,但背后却隐藏着复杂的AI技术。今天,我们就来揭秘BOTA六维力矩传感器在机器人操控…

macOS Chrome - 打开开发者工具,设置 Local storage

文章目录 macOS Chrome - 打开开发者工具设置 Local storage macOS Chrome - 打开开发者工具 方式2:右键点击网页,选择 检查 设置 Local storage 选择要设置的 url,显示右侧面板 双击面板,输入要添加的内容 2025-04-08&#xff…

kubernetes 入门篇之架构介绍

经过前段时间的学习和实践,对k8s的架构有了一个大致的理解。 1. k8s 分层架构 架构层级核心组件控制平面层etcd、API Server、Scheduler、Controller Manager工作节点层Kubelet、Kube-proxy、CRI(容器运行时接口)、CNI(网络插件&…

如何使用通义灵码完成PHP单元测试 - AI辅助开发教程

一、引言 在软件开发过程中,测试是至关重要的一环。然而,在传统开发中,测试常常被忽略或草草处理,很多时候并非开发人员故意为之,而是缺乏相应的测试思路和方法,不知道如何设计测试用例。随着 AI 技术的飞…

pig 权限管理开源项目学习

pig 源码 https://github.com/pig-mesh/pig 文档在其中,前端在文档中,官方视频教学也在文档中有。 第一次搭建,建议直接去看单体视频,照着做即可,需 mysql,redis 基础。 文章目录 项目结构Maven 多模块项…

探秘Transformer系列之(26)--- KV Cache优化 之 PD分离or合并

探秘Transformer系列之(26)— KV Cache优化 之 PD分离or合并 文章目录 探秘Transformer系列之(26)--- KV Cache优化 之 PD分离or合并0x00 概述0x01 背景知识1.1 自回归&迭代1.2 KV Cache 0x02 静态批处理2.1 调度策略2.2 问题…

C++语言程序设计——02 变量与数据类型

目录 一、变量与数据类型(一)变量的数据类型(二)变量命名规则(三)定义变量(四)变量赋值(五)查看数据类型 二、ASCII码三、进制表示与转换(一&…