测试硬件条件:stm32F1系列(CM3内核)
软件编译条件:MDK5
库函数:F1(官方给定库)
1、void KEY_Init(void):KEY_Init()是用来 初始化按键输入的 IO 口的。首先使能 GPIOA 和 GPIOE 时钟,然后实现 PA0、PE2~4 的输入设置。
2、KEY_Scan()函数,则是用来扫描这 4 个 IO 口是否有按键按下。KEY_Scan()函数,支持两种扫描方式,通过 mode 参数来设置。
3、mode 这个参数,大家就可以根据自己的需要,选择不同的方式。这里要提醒大家, 因为该函数里面有 static 变量,所以该函数不是一个可重入函数,在有 OS 的情况下,这个大家 要留意下。同时还有一点要注意的就是,该函数的按键扫描是有优先级的,最优先的是 KEY0, 第二优先的是 KEY1,接着 KEY2,最后是 WK_UP 按键。该函数有返回值,如果有按键按下,则返回非 0 值,如果没有或者按键不正确,则返回 0。(尤其要注意下程序的结构,else if 后面的两个程序句,紧跟着的为判断是真的,下一个为判断为假的)
前言:STM32 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32 的强大之处。STM32F103 的中断控制器支持 19 个外部中断/ 事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F103 的 19 个外部中断为:
线0~15:对应外部 IO 口的输入中断。
线16:连接到 PVD 输出。
线17:连接到 RTC 闹钟事件。
线18:连接到 USB 唤醒事件。
GPIO 跟中断线的映射关系图:
IO 口外部中断的一般步骤:
1)初始化 IO 口为输入。
2)开启 AFIO 时钟 。
3)设置 IO 口与中断线的映射关系。
4)初始化线上中断,设置触发条件等。
5)配置中断分组(NVIC),并使能中断。
6)编写中断服务函数。
中断函数的对应关系:
中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中 断线 10-15 共用中断函数 EXTI15_10_IRQHandler。
在编写中断服务函数的时候会经常使用到两 个函数,第一个函数是判断某个中断线上的中断是否发生(标志位是否置位): ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); 这个函数一般使用在中断服务函数的开头判断中断是否发生。另一个函数是清除某个中断线上 的中断标志位: void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
注意点:函数 EXTI_GetFlagStatus 和 EXTI_ClearFlag,他们的作用和前面两个函数的作用类似。 只是在 EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而 EXTI_GetFlagStatus 直接用来判断状态标志位。
例子:
void EXTI3_IRQHandler(void)
{ if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{ 中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
} }
前言:独立看门狗由内部专门的 40Khz 低速时钟驱动,即使主时钟发生故障,它也仍然有效。并不是准确的 40Khz,而是在 30~60Khz 之间的一个可变化的时钟,只是我们在估算的时候,以 40Khz 的频率来计算,看门狗对时间的要求不是很精确。
单片机系统在外界的干扰下会出现程序跑飞的现象导致出现死循环,看门狗电路就是为了避免 这种情况的发生。看门狗的作用就是在一定时间内(通过定时计数器实现)没有接收喂狗信号 (表示 MCU 已经挂了),便实现处理器的自动复位重启(发送复位信号) 。
相关的寄存器介绍:键值寄存器 IWDG_KR、预分频寄存器(IWDG_PR)、看门狗的重装载值(IWDG_RLR )
使用独立看门狗的步骤如下:每一步骤对应一个函数
1)取消寄存器写保护(向 IWDG_KR 写入 0X5555)
2)设置独立看门狗的预分频系数和重装载值
Tout=((4×2^prer) ×rlr) /40 其中 Tout 为看门狗溢出时间(单位为 ms);
prer 为看门狗时钟预分频值(IWDG_PR 值),范围为 0~7;
rlr 为看门狗的重装载值(IWDG_RLR 的值);
3)重载计数值喂狗(向 IWDG_KR 写入 0XAAAA)
4) 启动看门狗(向 IWDG_KR 写入 0XCCCC)
注意点:启动 STM32 的看门狗。注意 IWDG 在一旦启用,就不能再被关闭!想要关闭,只能重启,并且重启之后不能打开 IWDG,否则问题依旧,所以在这里提醒大家,如果不 用 IWDG 的话,就不要去打开它,免得麻烦。
void IWDG_Init(u8 prer,u16 rlr) {
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //①使能对寄存器 I 写操作
IWDG_SetPrescaler(prer); //②设置 IWDG 预分频值:设置 IWDG 预分频值
IWDG_SetReload(rlr); //②设置 IWDG 重装载值
IWDG_ReloadCounter(); //③按照 IWDG 重装载寄存器的值重装载 IWDG 计数器
IWDG_Enable(); //④使能 IWDG
}
//喂独立看门狗
void IWDG_Feed(void) { IWDG_ReloadCounter();//reload }
前言:窗口看门狗是利用APB1的时钟来计数,在程序跑飞后实行软复位。
涉及寄存器:递减计数器WWDG->CR 、窗口配置寄存器(WWDG->CFR)、状态寄存器(WWDG_SR)
原理:
T[6:0]就是 WWDG_CR 的低七位,W[6:0]即是 WWDG->CFR 的低七位。T[6:0] 就是窗口看门狗的计数器,而 W[6:0]则是窗口看门狗的上窗口,下窗口值是固定的(0X40)。 当窗口看门狗的计数器在上窗口值之外被刷新,或者低于下窗口值都会产生复位。 上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但是一定要确保 窗口值大于 0X40,否则窗口就不存在了。
WWDG_CR寄存器
注意点:WDGA 位则是看门狗的激活位,该位由软件置 1,以启动看门狗,并且一定要注意的是该 位一旦设置,就只能在硬件复位后才能清零了。
使用步骤:
1)使能 WWDG 时钟 2)设置窗口值和分频数 3)开启 WWDG 中断并分组 4) 设置计数器初始值并使能看门狗
5) 编写中断服务函数 ——》通过该函数来喂狗,喂狗要快,否则当窗口看门狗计数器值减到 0X3F 的时候,就会引起软复位了。在中断服务函数里面也要将状态 寄存器的 EWIF 位清空。
void WWDG_IRQHandler(void) {
WWDG_SetCounter(WWDG_CNT); //当禁掉此句后,窗口看门狗将产生复位
WWDG_ClearFlag(); //清除提前唤醒中断标志位
LED1=!LED1;
//LED 状态翻转 }
前言:本节介绍通用定时器的功能,基本上由可编程预分频器驱动的16位自动装载的计数器构成。STM32 的每个通用定时器都是完全独立的, 没有互相共享的任何资源。
1)16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~ 65535 之间的任意数值。
3)4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获 B.输出比较 C.PWM 生成(边缘或中间对齐模式) D.单脉冲模式输出
寄存器集介绍:
控制寄存器 1(TIMx_CR1)
DMA/中断使能寄存器 (TIMx_DIER)
预分频寄存器(TIMx_PSC)
注意:根据图中的计算公式,定时器的时钟来源有 4 个,本次选用了APB1,其中高级定时器的时钟来源为APB2。
定时器的计数器寄存器 :TIMx_CNT
自动重装载寄存器(TIMx_ARR)
该寄存器在物理上实际对应着 2 个寄存器。 一个是程序员可以直接操作的,另外一个是程序员看不到的,这个看不到的寄存器叫做影子寄存器。事实上真正起作用的是影子寄存器。根据 TIMx_CR1 寄存 器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2 者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。
状态寄存器(TIMx_SR)
该寄存器用来标记当前与定时 器相关的各种事件/中断是否发生。
定时器中断使用步骤:
1)TIM3 时钟使能。
2)初始化定时器参数,设置自动重装值,分频系数,计数方式等。 定时器的初始化参数是通过初始化函数 TIM_TimeBaseInit 实现的
3)设置 TIM3_DIER 允许更新中断。
4)TIM3 中断优先级设置。
5)允许 TIM3 工作,也就是使能 TIM3。
6)编写中断服务函数。
要点:在前面时钟系统部分我们讲解过,系统初始化的时候在默认的系统初始化函数SystemInit函数里面已经初始化APB1的时钟为2分频, 所以 APB1 的时钟为 36M(怎么查看每一条线上的时钟大小——》STM32 时钟系统的配置除了初始化的时候在 system_stm32f10x.c 中的 SystemInit()函数中外,其他的配置主要在 stm32f10x_rcc.c 文件中)
而从 STM32 的内部时钟树图得知:当 APB1 的时钟分频数为 1 的 时候,TIM2~7 的时钟为 APB1 的时钟,而如果 APB1 的时钟分频数不为 1,那么 TIM2~7 的时 钟频率将为 APB1 时钟的两倍。因此,TIM3 的时钟为 72M,再根据我们设计的 arr 和 psc 的值,就可以计算中断时间了。计算公式如下: Tout= ((arr+1)*(psc+1))/Tclk; 其中:
Tclk:TIM3 的输入时钟频率(单位为 Mhz)。 Tout:TIM3 溢出时间(单位为 us)
在定时器的基础上,还需要包含以下寄存器:
捕获/比较模式寄存器 (TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)
操作步骤:
1)开启 TIM3 时钟以及复用功能时钟,配置 PB5 为复用输出。(需要复用时再打开)
3)初始化 TIM3,设置 TIM3 的 ARR 和 PSC。 (PWM 通道设置是通过函数 TIM_OC1Init()~TIM_OC4Init()来设置的)
4)设置 TIM3_CH2 的 PWM 模式,使能 TIM3 的 CH2 输出。
5)使能 TIM3。
6)修改 TIM3_CCR2 来控制占空比。 TIM3_CCR2 则可以控制 CH2 的输出占空比
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
其中所有需要的寄存器前面都已经讲过了,主要是针对模式寄存器和使能寄存器进行相应的参数设置。
捕获/比较使能寄存器:TIMx_CCER,
本章我们要用到这个寄存器的最低 2 位,CC1E 和 CC1P 位。
使用步骤中的关键点:
3)设置 TIM5 的输入比较参数,开启输入捕获
库函数是通过 TIM_ICInit 函数来初始化输入比较参数的
4)使能捕获和更新中断(设置 TIM5 的 DIER 寄存器)
STM32 的低功耗模式有 3 种:
1)睡眠模式(CM3 内核停止,外设仍然运行)
2)停止模式(所有时钟都停止)
3)待机模式(1.8V 内核电源关闭)
最低功耗是待机模式,然后是停机模式。
进入待机模式的过程步骤:
介绍了多种可退出待机模式的操作。
涉及的电源状态寄存器有:即电源控制寄存器(PWR_CR)和电源控制/状态寄存器(PWR_CSR)
程序流程:
1)使能电源时钟。
2) 设置 WK_UP 引脚作为唤醒源。
3)设置 SLEEPDEEP 位,设置 PDDS 位,执行 WFI 指令,进入待机模式。
注意:进行上面三个功能进入待机模式是在函数PWR_EnterSTANDBYMode 中实现的:
void PWR_EnterSTANDBYMode(void);
4)最后编写 WK_UP 中断函数。
前言:STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC 可以独立使用, 也可以使用双重模式(提高采样率)。STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。
它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫 描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。
STM32F103 系列最少都拥有 2 个 ADC,我们选择的 STM32F103ZET 包含有 3 个 ADC。 STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期 为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。
STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于正常运行的程序,而注入通道呢,就相当于中断。在程序正常执行的时候,中断是可以打断执行的。
同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
注意:STM32 其 ADC 的规则通道组最多包含 16 个转换,而注入通道组最多包含 4 个通道。
寄存器介绍:
ADC 控制寄存器(ADC_CR1 和 ADC_CR2)
ADON 位用于开关 AD 转换器
EXTSEL[2: 0]用于选择启动规则转换组转换的外部事件
ADC 采样事件寄存器(ADC_SMPR1 和 ADC_SMPR2)
这两个寄存器 用于设置通道 0~17 的采样时间,每个通道占用 3 个位
ADC 的转换时间可以由以下公式计算:
Tcovn=采样时间+12.5 个周期
其中:Tcovn 为总转换时间,采样时间是根据每个通道的 SMP 位的设置来决定的。
例如, 当 ADCCLK=14Mhz 的时候,并设置 1.5 个周期的采样时间,则得到:Tcovn=1.5+12.5=14 个周 期=1us。
ADC 规则序列寄存器(ADC_SQR1~3)
注意:我们选择的是单次转换, 所以只有一个通道在规则序列里面,这个序列就是 SQ1,通过 ADC_SQR3 的最低 5 位(也就是 SQ1)设置 。
是 ADC 规则数据寄存器(ADC_DR)、ADC_JDRx
规则序列中的 AD 转化结果都将被存 在这个寄存器里面,而注入通道的转换结果被保存在 ADC_JDRx 里面
ADC 寄存器为 ADC 状态寄存器(ADC_SR),该寄存器保存了 ADC 转 换时的各种状态。
程序初始化使用步骤:
1)开启 PA 口时钟和 ADC1 时钟,设置 PA1 为模拟输入。
2)复位 ADC1,同时设置 ADC1 分频因子。 注意:通过 RCC_CFGR 设置 ADC1 的分频因子。分频因子要确保 ADC1 的时钟(ADCCLK) 不要超过 14Mhz。 这个我们设置分频因子位 6,时钟为 72/6=12MHz 。
3)初始化 ADC1 参数,设置 ADC1 的工作模式以及规则序列的相关信息。 注意:设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。
5)使能 ADC 并校准。 在设置完了以上信息后,我们就使能 AD 转换器,执行复位校准和 AD 校准,注意这两步 是必须的!不校准将导致结果很不准确。
6)读取 ADC 值。 设置规则序列 里面 的通道,采样顺序,以及通道的采样周期,然后启动 ADC 转换。
软件开启 ADC 转换的方法是:ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的软件转换启动功能
推荐文章超详细:https://blog.csdn.net/qq_38410730/article/details/80071349
前言:大容量的 STM32F103 具有内部 DAC,DAC 模块有 2 个输出通道,每个通道都有单独的转换器。在双 DAC 模式下,2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出。
① 2 个 DAC 转换器:每个转换器对应 1 个输出通道
② 8 位或者 12 位单调输出
③ 12 位模式下数据左对齐或者右对齐
④ 同步更新功能 ⑤ 噪声波形生成 ⑥ 三角波形生成 ⑦ 双 DAC 通道同时或者分别转换 ⑧ 每个通道都有 DMA 功能
DAC_OUTx 就是 DAC 的输出通道了(对应 PA4 或者 PA5 引脚)
DAC输出是受DORx寄存器直接控制的,但是我们不能直接往DORx 寄存器写入数据,而是通过 DHRx 间接的传给 DORx 寄存器
STM32 的 DAC 支持 8/12 位模式,8 位模式的时候是固定的右对齐的,而 12 位模式 又可以设置左对齐/右对齐。单 DAC 通道 x,总共有 3 种情况:
① 8 位数据右对齐:用户将数据写入 DAC_DHR8Rx[7:0]位(实际是存入 DHRx[11:4]位)
② 12 位数据左对齐:用户将数据写入 DAC_DHR12Lx[15:4]位(实际是存入 DHRx[11:0] 位)
③ 12 位数据右对齐:用户将数据写入 DAC_DHR12Rx[11:0]位(实际是存入 DHRx[11:0] 位)
12 位模式下 DAC 输出电压与 Vref+以及 DORx 的计算公式如下: DACx 输出电压=Vref*(DORx/4095)
注意:DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2
在 DAC_CR 设置好之后,DAC 就 可以正常工作了,我们仅需要再设置 DAC 的数据保持寄存器的值:
程序流程步骤:
1)开启 PA 口时钟,设置 PA4 为模拟输入。
置 PA4 为模拟输入。DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但 使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设 置为输入,是为了避免额外的干扰。
2)使能 DAC1 时钟。
3)初始化 DAC,设置 DAC 的工作模式。
4)使能 DAC 转换通道 。
5)设置 DAC 的输出值。
通过设置 DHR12R1,就可以在 DAC 输出引脚(PA4)得到不同的电压值了。库函数的函数是: DAC_SetChannel1Data(DAC_Align_12b_R, 0); 第一个参数设置对齐方式,
可以为 12 位右对齐 DAC_Align_12b_R,12 位左对齐 DAC_Align_12b_L 以及 8 位右对齐 DAC_Align_8b_R 方式。
还可以读出 DAC 的数值,函数是: DAC_GetDataOutputValue(DAC_Channel_1);
虽然大容量的STM32F103具有内部DAC,但是更多的型号是没有DAC的,不过STM32 所有的芯片都有 PWM 输出,因此,我们可以用 PWM+简单的 RC 滤波来实现 DAC 输出, 从而节省成本。
形成原理:
未完待续.....持续更新中,欢迎有问题留言交流