一、STM32系统结构
AHB系统总线:挂载的一般是最基本的或者性能比较高的外设
APB2、APB1:两个外设总线,APB2一般和AHB同频率,都是72MHz,APB1一般是36MHz
APB:先进外设总线,用于连接一般的外设
DMA:用来干类似数据搬运这种简单且反复要干的活
二、引脚定义
颜色标记:
红色 | 蓝色 | 绿色 |
---|---|---|
电源相关引脚 | 最小系统相关引脚 | IO口、功能口引脚 |
FT:Five voltage Tolerant,就是5V容忍
主功能:上电后默认的功能,一般和引脚名称相同。如果不同,引脚实际功能是主功能,而不是引脚名称的功能
默认复用功能:IO口上同时连接的外设功能引脚(配置IO口时可以选择通用IO口还是复用功能)
表中加粗IO口和没加粗IO口:推荐优先使用加粗IO口,没有加粗的IO口可能需要配置或者兼具其他功能,使用时需要留意一下
34号 + 37 ~ 40号:这些是IO口或者调试端口,默认主功能是调试端口
三、EXTI外部中断
1、抢占优先级、响应优先级
响应优先级:
上一个病人在看病,外面排队了很多病人。当上一个病人看完后,紧急的病人即使是后来的,也会最先进去看病。
抢占优先级:
上一个病人正在看病,还没看完,这时来了一个更加紧急的病人,那他可以不等上个人看完,直接冲到医生屋里,让上一个病人先靠边站,先给他看,给他看完了,再给上一个病人看。这种决定是不是可以中断嵌套的优先级,就叫抢占优先级。
2、NVIC优先级分组
NVIC的中断优先级由优先级寄存器的4位(十进制:0-15,二进制:0-1111)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
分组方式:
分组方式 | 抢占优先级 | 响应优先级 |
---|---|---|
分组0 | 0位,取值为0 | 4位,取值为0~15(二进制:0-1111) |
分组1 | 1位,取值为0~1 | 3位,取值为0~7(二进制0-111) |
分组2 | 2位,取值为0~3 | 2位,取值为0~3(二进制:0-11) |
分组3 | 3位,取值为0~7 | 1位,取值1为0~1 |
分组4 | 4位,取值为0~15 | 0位,取值为0 |
EXTI简介:
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时, EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿(上升、下降都可以)/软件触发(程序触发)
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发
中断通道数: 16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
触发响应方式:中断响应/事件响应
EXTI基本结构:
注意:外部中断9~5会触发同一个中断函数,15-10也会触发同一个中断函数。在编程的时候我们需要再根据标志位来区分到底是哪个中断进来的。
四、TIM定时器
1、基本定时器
预分频器:对72MHz的计数时钟进行预分频。这个寄存器写0,就是不分频(或叫做一分频),此时 输出频率 = 输入频率 = 72MHz。如果写1,那就是2分频,输出频率 = 输入频率/2 = 36MHz。以此类推……这个预分频器是16位的,所以最大写65535,也就是65536分频
注:若当计数记到一半的时候改变了分频值,这个变化不会立即生效(此时计数频率仍然保持为原来的频率),而是会等到本次计数周期结束时,产生了更新事件,预分频寄存器的值才会被传递到缓冲寄存器里面去,才会生效(上一轮计数完成后,在下一轮计数时,改变后的分频值才会起作用)。
计数器:对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值加1。计数器也是16位的,所以里面的值可以从0一直加到65535,如果再加的话,计数器会回到0重新开始
自动重装寄存器:它存的是我们写入的计数目标。同样是16位的。在运行过程中,计数值不断自增,自动重装值是固定目标。当计数值等于自动重装值时,也就是计时时间到了,它产生一个中断信号(通往NVIC),并且清零计数器,计数器自动开始下一次的计数计时
2、通用定时器
计数模式:还支持向下计数模式和中央对齐模式
向下计数模式:从重装值开始,向下自减,减到0之后,回到重装值(直接回到重装值,不是再自增上去)同时申请中断,然后继续下一轮,依次循环
中央对齐计数模式:从0开始,先向上自增,记到重装值,申请中断。然后向下自减,减到0,再申请中断。然后进行下一轮,依次循环
内外时钟源选择:不仅可以选择内部的72MHz时钟,还可以选择外部时钟(来自TIMx_ETR引脚上的外部时钟)
3、定时器初始化
只需要把这里面的每个模块都打通,就可以让定时器工作了。
大体步骤:
第一步:RCC开启时钟,定时器基准时钟和整个外设的工作时钟就都会同时打开了。
第二步:选择时基单元的时钟源,对于定时中断,我们就选择内部时钟源
第三步:配置时基单元,包括预分频器、自动重装器、计数模式等等(用一个结构体配置)
第四步:配置输出中断控制,允许更新中断输出到NVIC
第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
第六步:运行控制,使能计数器
定时器库函数介绍:
void TIM_DeInit(TIM_TypeDef* TIMx);
恢复缺省配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
时基单元初始化
两个参数:
第一个TIMx选择某个定时器。
第二个是结构体,里面包含了配置时基单元的一些参数
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
把结构体变量赋一个默认值
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
使能计数器(对应图里的运行控制)
两个参数:
第一个选择定时器。
第二个状态,使能,计数器运行,失能,计数器不运行
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
用来使能中断输出信号(对应图中中断输出控制)
参数:
第一个:选择定时器。
第二个:选择配置哪个中断输出。
第三个:使能还是失能。
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
这六个函数对应时基单元的时钟选择部分
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
选择ITRx其他定时器的时钟
参数:
TIMx:选择要配置的定时器。
第二个:选择要接入哪个其它的定时器。
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
选择TIx捕获通道的时钟
参数:
TIMx:选择要配置的定时器。
第二个:选择TIx具体的某个引脚。
ICPolarity和ICFilter:输入的极性和滤波器。
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
选择ETR通过外部时钟模式一输入的时钟
参数:
ExitTRGPrescaler:外部触发预分频器,对ETR的外部时钟再提前做一个分频。
Polarity和Filter:极性和滤波器。
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
同上
对于ETR输入的外部时钟而言,这两个函数是等效的,如果不需要输入触发的功能,那两个函数可以互换。
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
不是用来选择时钟的,是用来单独配置ETR引脚的预分频器、极性、滤波器这些参数的
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
用来单独写预分频值
参数:
Prescaler:预分频值。
PSCReladMode:写入的模式,可以选择听从安排,在更新事件后生效,或者是,在写入后,手动产生一个更行事件,让这个值立即生效
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
用来改变计数器的计数模式
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
自动重装器预装功能配置
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
给计数器写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
给自动重装器写入一个值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
获取当前预分频器的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
用来获取标志位和清除标志位的四个函数