吧唧吧唧的吃. . . . . . . . .

STM32学习笔记


STM32学习笔记

一、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);
用来获取标志位和清除标志位的四个函数

文章作者: 668rose
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 668rose !
评论
  目录