STM32笔记

STM32笔记

SWD连接开发板

什么是SWD

SWD与JTAG同属调试接口,是芯片在设计之初就预制的对芯片进行开发调试并在开发板上预留的接口,

JTAG接口

目前开发板上的接口大多是20PIN的,与此对应的关系如下:
JTAP20PIN.png

DAPLink在JTAG接口连接

只用连接3根线 SWD、SWCLK、GND。对应关系为
img
连接时要注意将接口摆放与图片一致后接线,这样不容易弄反

GPIO

通用输入输出接口

GPIO_MODE

(1)GPIO_Mode_AIN 模拟输入
模式:GPIO_Mode_AIN
   TTL肖特基触发器
(2)GPIO_Mode_IN_FLOATING 浮空输入
模式:GPIO_Mode_IN_FLOATING
   上、下拉都不启用
(3)GPIO_Mode_IPD 下拉输入
模式:GPIO_Mode_IPD默认低电平
   下拉电阻启用
(4)GPIO_Mode_IPU 上拉输入
模式:GPIO_Mode_IPU 默认高电平
   上拉电阻启用

小结
上拉输入模式:区别在于没有输入信号的时候默认输入高电平(因为有弱上拉)。下拉输入模式:区别在于没有输入信号的时候默认输入低电平(因为有弱下拉)。对于浮空输入模式顾名思义也就是输入什么信号才是什么信号,对于浮空输入要保证有明确的输入信号。

(5)GPIO_Mode_Out_OD 开漏输出
模式:GPIO_Mode_Out_OD
   开漏:有一极处于开路状态(或称为高阻态)

img

(6)GPIO_Mode_Out_PP 推挽输出
模式:GPIO_Mode_Out_PP
   :使用单片机电压推送高电源电压
   :使用低电压把外面电流拉进来

img

小结:

推挽输出 开漏输出
高电平 P-MOS激活N-MOS断开,3.3V P-MOS断开N-MOS断开,外部决定
低电平 P-MOS断开N-MOS激活,0V P-MOS断开N-MOS激活,0V
优点 直接输出3.3V 配合外部电路更加灵活
缺点 只能输出3.3V 高电平是高阻态,无输出能力

(7)GPIO_Mode_AF_OD 复用开漏输出
模式:GPIO_Mode_IPD
   管脚复用状态下的开漏输出
(8)GPIO_Mode_AF_PP 复用推挽输出
模式:GPIO_Mode_IPD
   管脚复用状态下的推挽输出

GPIO库函数简单说明

  1. GPIO_DeInit 重新初始化外围设备GPIOx相关寄存器到它的默认复位值
  2. GPIO_AFIODeInit 初始化交错功能(remap, event control和 EXTI 配置) 寄存器
  3. GPIO_Init 根据GPIO_初始化结构指定的元素初始化外围设备GPIOx
  4. GPIO_StructInit 填充GPIO_初始化结构(GPIO_InitStruct)内的元素为复位值
  5. GPIO_ReadInputDataBit 读指定端口引脚输入数据
  6. GPIO_ReadInputData 读指定端口输入数据
  7. GPIO_ReadOtputDataBit 读指定端口引脚输出数据
  8. GPIO_ReadOtputData 读指定端口输出数据
  9. GPIO_SetBits 置1指定的端口引脚
  10. GPIO_ResetBits 清0指定的端口引脚
  11. GPIO_WriteBit 设置或清除选择的数据端口引脚
  12. GPIO_Write 写指定数据到GPIOx端口寄存器
  13. GPIO_ANAPinConfig 允许或禁止 GPIO 4 模拟输入模式
  14. GPIO_PinLockConfig 锁定GPIO引脚寄存器
  15. GPIO_EventOutputConfig 选择GPIO引脚作为事件输出
  16. GPIO_EventOutputCmd 允许或禁止事件输出
  17. GPIO_PinRemapConfig 改变指定引脚的影射
  18. GPIO_EMIConfig 允许或禁止GPIO 8 和 9 的EMI 模式

PNP继电器

继电器原理图如下:
img

1. 工作状态分析
当PE2管脚输出为低电压,PNP三极管发射极(2号管脚)与集电极(3号管脚)导通,PNP三极管的发射极(2号管脚)与基极(1号管脚)导通,此时继电器处于工作状态,LED被点亮。此时继电器JDQ工作(因两侧存在电压差,2号管脚高),此时电流方向为2->1、2->3。
2. 静止状态分析
当PE2管脚为开路时,PNP三极管发射极(2号管脚)与基极(1号管脚)不存在电压差,此时电流被断开,不存在电流。继电器关闭,LED不点亮。

上拉电阻、下拉电阻、不上拉也不下拉

1、上拉电阻: 将电路连接到外部电源,提升电压。常用于漏极开路输出
2、不上拉也不下拉: 电路无变化连接到外部电源,常用于推挽输出
3、下拉电阻: 输入低电平

继电器与按钮Demo

案例实现点击key0继电器吸合两秒后自动关闭

mian.c

#include "sys.h"	
#include "delay.h"	
#include "led.h" 
#include "jdq.h" 
#include "key.h"

 int main(void)
 {
	delay_init();	    	 //延时函数初始化	  
	LED_Init();		  	 	//初始化与LED连接的硬件接口
	JDQ_Init();         	//初始化蜂鸣器端口
	Key_Init();
	while(1)
	{	
		LED1 =0;
		JDQ = 1;
		delay_ms(100);
		if(KEY0 == 0){
			LED0 = 0;
			JDQ = 0;
			KEY0 = 1;
			delay_ms(1000);
			delay_ms(1000);
		}
		LED0 = 1;
		JDQ = 1;
	}
 }

led.c

#include "led.h"

//初始化PB5和PE5为输出口.并使能这两个口的时钟		    
//LED IO初始化
void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);						 //PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		 //LED1-->PE.5 端口配置, 推挽输出
 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 //PE.5 输出高 
}

jdq.c

#include "jdq.h"


//初始化PE2为输出口.并使能这个口的时钟		    
//继电器初始化
void JDQ_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);	 //使能GPIOE端口时钟
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;				 //JDQ-->PE.2 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; 		 //开漏输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //速度为50MHz
 GPIO_Init(GPIOE, &GPIO_InitStructure);	 //根据参数初始化GPIOE.2
 
 GPIO_ResetBits(GPIOE,GPIO_Pin_2);//设置继电器关闭

}


优化按钮捕捉,检测按钮是否被处于空状态,代码编写提示、对按钮全过程检测

#include "sys.h"	
#include "delay.h"	
#include "led.h" 
#include "jdq.h" 
#include "key.h"

int getKey0Static(void);

 int main(void)
 {
	delay_init();	    	 //延时函数初始化	  
	LED_Init();		  	 	//初始化与LED连接的硬件接口
	JDQ_Init();         	//初始化蜂鸣器端口
	Key_Init();
	LED1 =0;
	JDQ = 1;
	while(1)
	{	
			int key0Static = getKey0Static();
			if( key0Static == 1){
					JDQ=~JDQ;
			}
	}
 }

int getKey0Static(void){
	static int key_up = 1;//按钮原位置
		
		if(key_up&&KEY0 == 0){
			delay_ms(30);
			if(KEY0 ==0){
				key_up = 0;
				return 1;
			}
			return 0;
		}else if(KEY0==1){
			key_up = 1;
			return 0;
		}
		return 0;
}

C语言按位操作

>>= 右移后赋值

例如 x=0x08 x>>=3
计算步骤如下:

  1. x=0000 1000
  2. 右移3位0000 0001
  3. 赋值后为0000 0001
  4. 结果x=0x01

<<= 左移后赋值

例如 x=0x08 x<<=3

  1. x=0000 1000
  2. 左移三位 0100 0000
  3. 赋值后为 0100 0000
  4. 结果 x=0x40

&= 按位与后赋值

例如 x=0x08 x &= 0x03

  1. x = 0000 1000
  2. 0x03=0000 0011
  3. end 0000 0000
  4. 赋值后为 0000 0000
  5. x=0x00

^= 按位异或后赋值

异或:两个不一样取1,一样取0
例如 x=0x09 x ^= 0x03

  1. x = 0000 1001
  2. 0x03=0000 0011
  3. end 0000 1010
  4. 赋值后为 0000 1010
  5. 结果x=0x0A

|= 按位或后赋值

有一个1就取1,两个都为0才取0
例如 x=0x08 x |= 0x03

  1. x = 0000 1000
  2. 0x03=0000 0011
  3. end 0000 1011
  4. 赋值后为 0000 1011
  5. 结果x=0x0B

小结

  • 或等于(|=)常用于对某一位赋值,因其后给的数与原数或后原数的1保持不变,只改变其他为0的位,
  • 与等于(&=)常用来对某一位或多个位清零,引起有0就为0,只有两个1才不为0的特性。要保留1位的就赋1,清除的就赋0

时钟树

为什么要设置时钟,一是寄存器是由D触发器组成的,只有送来了时钟,触发器才能被改写值,这样寄存器才能工作;二是现在的功能过多功耗顶不住,所以现在的芯片是使用那个功能就对时钟和串口使能用哪个功能。

img

简述

  1. HSE、LSE分别为高速和低速外部时钟,一般为晶振产生。高速外部时钟输入范围为4-16MHz,低速外部时钟为定频40kHz
  2. HSI、LSI分别为高速和低速内部时钟,一般为内部RC震荡电路产生受温度影响。高速内部时钟为定频8MHz,低速内部时钟为定频32.768kHz
  3. 红圈2为选择器,选择应用哪个源作为输出时钟信号
  4. 红圈3为倍频器,将信号倍频x2~x16,
  5. 红圈5为分频器,将信号分频为/1、/2、/4、/8、/16、/64、/128、/256

设置系统时钟库函数

static void SetSysClockTo72(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    // ① 使能HSE,并等待HSE稳定
    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    // 等待HSE启动稳定,并做超时处理
    do {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0)
        &&(StartUpCounter !=HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
        HSEStatus = (uint32_t)0x01;
    } else {
        HSEStatus = (uint32_t)0x00;
    }
    // HSE启动成功,则继续往下处理
    if (HSEStatus == (uint32_t)0x01) {

        //-----------------------------------------------------------
        // 使能FLASH 预存取缓冲区 */
        FLASH->ACR |= FLASH_ACR_PRFTBE;

        // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
        // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
        // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
        // 0:0 < SYSCLK <= 24M
        // 1:24< SYSCLK <= 48M
        // 2:48< SYSCLK <= 72M */
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
        //------------------------------------------------------------

        // ② 设置AHB、APB2、APB1预分频因子
        // HCLK = SYSCLK
        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        //PCLK2 = HCLK
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        //PCLK1 = HCLK/2
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

        // ③ 设置PLL时钟来源,设置PLL倍频因子,PLLCLK = HSE * 9 = 72 MHz
        RCC->CFGR &= (uint32_t)((uint32_t)
                                ~(RCC_CFGR_PLLSRC
                                | RCC_CFGR_PLLXTPRE
                                | RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE
                                | RCC_CFGR_PLLMULL9);

        // ④ 使能 PLL
        RCC->CR |= RCC_CR_PLLON;

        // ⑤ 等待PLL稳定
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
        }

        // ⑥ 选择PLL作为系统时钟来源
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;

        // ⑦ 读取时钟切换状态位,确保PLLCLK被选为系统时钟
        while ((RCC->CFGR&(uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){
        }
    } else {// 如果HSE启动失败,用户可以在这里添加错误代码出来
    }
}

中断

F103系统异常清单

img

stm32F103有8个系统异常(把Reset、HardFault算上有10个)

F103外部中断清单

位置 优先级 优先级类型 名称 说明 地址
0 7 可设置 WWDG 窗口定时器中断 0x0000_0040
1 8 可设置 PVD 连到EXTI的电源电压检测(PVD)中断 0x0000_0044
2 9 可设置 TAMPER 侵入检测中断 0x0000_0048
3 10 可设置 RTC 实时时钟(RTC)全局中断 0x0000_004C
4 11 可设置 FLASH 闪存全局中断 0x0000_0050
5 12 可设置 RCC 复位和时钟控制(RCC)中断 0x0000_0054
6 13 可设置 EXTI0 EXTI线0中断 0x0000_0058
7 14 可设置 EXTI1 EXTI线1中断 0x0000_005C
8 15 可设置 EXTI2 EXTI线2中断 0x0000_0060
9 16 可设置 EXTI3 EXTI线3中断 0x0000_0064
10 17 可设置 EXTI4 EXTI线4中断 0x0000_0068
11 18 可设置 DMA1通道1 DMA1通道1全局中断 0x0000_006C
12 19 可设置 DMA1通道2 DMA1通道2全局中断 0x0000_0070
13 20 可设置 DMA1通道3 DMA1通道3全局中断 0x0000_0074
14 21 可设置 DMA1通道4 DMA1通道4全局中断 0x0000_0078
15 22 可设置 DMA1通道5 DMA1通道5全局中断 0x0000_007C
16 23 可设置 DMA1通道6 DMA1通道6全局中断 0x0000_0080
17 24 可设置 DMA1通道7 DMA1通道7全局中断 0x0000_0084
18 25 可设置 ADC1_2 ADC1和ADC2的全局中断 0x0000_0088
19 26 可设置 USB_HP_CAN_TX USB高优先级或CAN发送中断 0x0000_008C
20 27 可设置 USB_LP_CAN_RX0 USB低优先级或CAN接收0中断 0x0000_0090
21 28 可设置 CAN_RX1 CAN接收1中断 0x0000_0094
22 29 可设置 CAN_SCE CANSCE中断 0x0000_0098
23 30 可设置 EXTI9_5 EXTI线[9:5]中断 0x0000_009C
24 31 可设置 TIM1_BRK TIM1刹车中断 0x0000_00A0
25 32 可设置 TIM1_UP TIM1更新中断 0x0000_00A4
26 33 可设置 TIM1_TRG_COM TIM1触发和通信中断 0x0000_00A8
27 34 可设置 TIM1_CC TIM1捕获比较中断 0x0000_00AC
28 35 可设置 TIM2 TIM2全局中断 0x0000_00B0
29 36 可设置 TIM3 TIM3全局中断 0x0000_00B4
30 37 可设置 TIM4 TIM4全局中断 0x0000_00B8
31 38 可设置 I2C1_EV I2C1事件中断 0x0000_00BC
32 39 可设置 I2C1_ER I2C1错误中断 0x0000_00C0
33 40 可设置 I2C2_EV I2C2事件中断 0x0000_00C4
34 41 可设置 I2C2_ER I2C2错误中断 0x0000_00C8
35 42 可设置 SPI1 SPI1全局中断 0x0000_00CC
36 43 可设置 SPI2 SPI2全局中断 0x0000_00D0
37 44 可设置 USART1 USART1全局中断 0x0000_00D4
38 45 可设置 USART2 USART2全局中断 0x0000_00D8
39 46 可设置 USART3 USART3全局中断 0x0000_00DC
40 47 可设置 EXTI15_10 EXTI线[15:10]中断 0x0000_00E0
41 48 可设置 RTCAlarm 连到EXTI的RTC闹钟中断 0x0000_00E4
42 49 可设置 USB唤醒 连到EXTI的从USB待机唤醒中断 0x0000_00E8
43 50 可设置 TIM8_BRK TIM8刹车中断 0x0000_00EC
44 51 可设置 TIM8_UP TIM8更新中断 0x0000_00F0
45 52 可设置 TIM8_TRG_COM TIM8触发和通信中断 0x0000_00F4
46 53 可设置 TIM8_CC TIM8捕获比较中断 0x0000_00F8
47 54 可设置 ADC3 ADC3全局中断 0x0000_00FC
48 55 可设置 FSMC FSMC全局中断 0x0000_0100
49 56 可设置 SDIO SDIO全局中断 0x0000_0104
50 57 可设置 TIM5 TIM5全局中断 0x0000_0108
51 58 可设置 SPI3 SPI3全局中断 0x0000_010C
52 59 可设置 UART4 UART4全局中断 0x0000_0110
53 60 可设置 UART5 UART5全局中断 0x0000_0114
54 61 可设置 TIM6 TIM6全局中断 0x0000_0118
55 62 可设置 TIM7 TIM7全局中断 0x0000_011C
56 63 可设置 DMA2通道1 DMA2通道1全局中断 0x0000_0120
57 64 可设置 DMA2通道2 DMA2通道2全局中断 0x0000_0124
58 65 可设置 DMA2通道3 DMA2通道3全局中断 0x0000_0128
59 66 可设置 DMA2通道4_5 DMA2通道4和DMA2通道5全局中断 0x0000_012C

NVIC

NVIC(Nested Vectored Interrupt Controller)嵌套向量中断控制器
M3原生NVIC支持共支持 1 至 240 个外部中断输入(通常外部中断写作 IRQs)。

结构体:
一般常用ISER用来使能中断,ICER用来失能中断,IP用来设置中断优先级。

typedef struct {
    __IO uint32_t ISER[8];       // 中断使能寄存器
    uint32_t RESERVED0[24];
    __IO uint32_t ICER[8];       // 中断清除寄存器
    uint32_t RSERVED1[24];
    __IO uint32_t ISPR[8];       // 中断使能悬起寄存器
    uint32_t RESERVED2[24];
    __IO uint32_t ICPR[8];       // 中断清除悬起寄存器
    uint32_t RESERVED3[24];
    __IO uint32_t IABR[8];       // 中断有效位寄存器
    uint32_t RESERVED4[56];
    __IO uint8_t  IP[240];       // 中断优先级寄存器(8Bit wide)
    uint32_t RESERVED5[644];
    __O  uint32_t STIR;          // 软件触发中断寄存器
}  NVIC_Type;

优先级

在NVIC 有一个专门的寄存器:中断优先级寄存器NVIC_IPRx,用来配置外部中断的优先级,IPR宽度为8bit, 原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。但是绝大多数CM3芯片都会精简设计, 以致实际上支持的优先级数减少,在F103中,只使用了高4bit,如下所示:
img

由此4个bit来设置优先级,stm32对4个bit的应用(或者说划分)做了排列组合并分成组如下

优先级分组 抢占优先级使用bit位数 抢占优先级对应级别范围 子优先级(响应优先级)使用bit数 子优先级(响应优先级)对应级别范围
NVIC_PriorityGroup_0 0 0 4 [0,15](2^4)
NVIC_PriorityGroup_1 1 [0,1] 3 [0,7](2^3)
NVIC_PriorityGroup_2 2 [0,3](2^2) 2 [0,3](2^2)
NVIC_PriorityGroup_3 3 [0,7](2^3) 1 [0,1]
NVIC_PriorityGroup_4 5 [0,15](2^4) 0 0

优先级比较顺序

  1. 抢占优先级(高先行)
  2. 子优先级(小先行)
  3. 硬件中断编号(小先行)

外部中断 EXTI

EXTI(External interrupt/event controller)管理了控制器的20个中断/事件线。 每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断/事件线进行单独配置, 可以单独配置为中断或者事件,以及触发事件的属性

功能框图

虽然还不懂,现在先加上以后懵懂了再说

EXTI功能框图
img

  1. 斜杠并标注“20”字样,这个表示在控制器内部类似的信号线路有20个
  2. EXTI可分为两大部分功能,一个是产生中断(红色线),另一个是产生事件(绿色线),这两个功能从硬件上就有所不同
  3. 编号1是输入线,EXTI控制器有19个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个GPIO
  4. 产生中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的
  5. 产生事件线路目的就是传输一个脉冲信号给其他外设使用, 并且是电路级别的信号传输,属于硬件级的

中断事件线

中断/事件线 输入源
EXTI[0-15] PnO(n为A、B、C、D、E、F、G、H、I)
EXTI16 PVD输出
EXTI17 RTC闹钟事件
EXTI18 USB唤醒事件
EXTI19 以太网唤醒事件(仅限互联网型)

库函数结构体 EXTI_InitTypeDef

typedef struct {
    uint32_t EXTI_Line;                 // 中断/事件线
    EXTIMode_TypeDef EXTI_Mode;         // EXTI模式
    EXTITrigger_TypeDef EXTI_Trigger;   // 触发类型
    FunctionalState EXTI_LineCmd;       // EXTI使能
} EXTI_InitTypeDef;
  1. EXTI_Line:EXTI中断/事件线选择,可选EXTI0至EXTI19,可参考表 EXTI中断_事件线 选择
  2. EXTI_Mode:EXTI模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)
  3. EXTI_Trigger:EXTI边沿触发事件,可选上升沿触发(EXTI_Trigger_Rising)、 下降沿触发( EXTI_Trigger_Falling)或者上升沿和下降沿都触发( EXTI_Trigger_Rising_Falling)
  4. EXTI_LineCmd:控制是否使能EXTI线,可选使能EXTI线(ENABLE)或禁用(DISABLE)

编程要点

  1. 初始化用来产生中断的GPIO;
  2. 初始化EXTI;
  3. 配置NVIC;
  4. 编写中断服务函数

中断函数与中断/事件线关系

在STM32中有启动文件 startup_stm32fXXX.s文件中有对应中断/事件线与中断函数名称之间关系已经规定好了,除此以外也可以通过宏定义方式按照自己的定义方式来写中断函数(这句不懂,我先跳过)。

外部中断摘抄

如图摘抄了外部中断0-15的中断线与对应中断函数的定义关系

posted @ 2023-08-02 23:24  伊芙利特  阅读(66)  评论(0编辑  收藏  举报