你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F1系列学习历程1(裸机全部程序)

[复制链接]
STMCU小助手 发布时间:2021-11-26 17:00
本文主要内容是针对库函数编程的STM32F1系列的初始化端口等外设的程序总结

测试硬件条件: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 r0 D5 e& c$ K4 S& |

定时器中断使用步骤:

1)TIM3 时钟使能。 8 I7 h, J- \8 V1 T2 q; @
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)

六、PWM输出

在定时器的基础上,还需要包含以下寄存器:

捕获/比较模式寄存器 (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 中断函数。

九、ADC原理与应用

前言: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]用于选择启动规则转换组转换的外部事件

6 v4 g& A5 b- N3 B3 \

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。  


) H) Y9 [2 H, m8 j8 c$ S

ADC 规则序列寄存器(ADC_SQR1~3)

注意:我们选择的是单次转换, 所以只有一个通道在规则序列里面,这个序列就是 SQ1,通过 ADC_SQR3 的最低 5 位(也就是 SQ1)设置 。


9 g, G& J# \. f0 U+ j, Q- \% _9 r/ E+ e) J! A

是 ADC 规则数据寄存器(ADC_DR)、ADC_JDRx

规则序列中的 AD 转化结果都将被存 在这个寄存器里面,而注入通道的转换结果被保存在 ADC_JDRx 里面

0 C9 A/ {9 H2 d6 f6 ^

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

十、DAC原理与应用

前言:大容量的 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]位)+ w" y) D/ c# S( u7 }$ E
② 12 位数据左对齐:用户将数据写入 DAC_DHR12Lx[15:4]位(实际是存入 DHRx[11:0] 位)

③ 12 位数据右对齐:用户将数据写入 DAC_DHR12Rx[11:0]位(实际是存入 DHRx[11:0] 位)

- F# u5 F* a+ F. {2 j

12 位模式下 DAC 输出电压与 Vref+以及 DORx 的计算公式如下: DACx 输出电压=Vref*(DORx/4095)

注意:DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2


$ j$ x" u/ ~1 I6 P8 {2 T

在 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);


. n9 O% V: A+ G十一、PWM+低通滤波电路——>DAC 简介

虽然大容量的STM32F103具有内部DAC,但是更多的型号是没有DAC的,不过STM32 所有的芯片都有 PWM 输出,因此,我们可以用 PWM+简单的 RC 滤波来实现 DAC 输出, 从而节省成本。

形成原理:


6 N  E! O. Z6 r, F9 x, _3 p, B

未完待续.....持续更新中,欢迎有问题留言交流

5 D1 B5 s1 A4 @$ J* v! `( G; ]. C/ E5 R
收藏 评论0 发布时间:2021-11-26 17:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版