STM32F1开发指南笔记6----SYSTEM文件夹__delay函数、sys函数、usart函数

此文件夹是STM32F10x系列的底层核心驱动函数,可以用在STM32F10x系列的各个型号上面,方便快速构建自己的工程。SYSTEM文件夹下包含看delay、sys、usart等三个文件夹。分别包含delay.c , sys.c , usart.c及其头文件。通过这3个C文件,可以快速的给任何一款STM32F1构建最基本的框架。

delay文件夹代码介绍

在这里插入图片描述
delay延时的编程思想:CM3内核的处理器,内部包含了一个systick定时器,systick是一个24位的倒计数定时器,当计数到0时,将从RELOAD寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在systick控制及状态寄存器中的使能位清除,就永不停息。
我们就是利用STM32的内部systick来实现延时的,这样既不占用中断,也不占用系统定时器。

我们以UCOSII的时钟为例,介绍如何实现操作系统和我们的delay函数共用systick定时器。首先,我们简单介绍下UCOSII的时钟:ucos运行需要一个系统时钟节拍(类似“心跳”),而这个节拍是固定的(由OS_TICKS_PER_SEC宏定义设置),比如要求5ms一次(即可设置:OS_TICKS_PER_SED=200),在STM32上面,一般是由systick来提供这个节拍,也就是systick要设置为5ms中断一次,为ucos提供时钟节拍,而且这个时钟一般是不能被打断的(否则就不准了)。

因为在ucos下systick不能再被随意更改,如果我们还想利用systick来做delay_us或者delay_ms的延时,就必须想点办法了,这里我们利用的是时钟摘取法。以delay_us为例,比如delay_us(50),在刚进入delay_us的时候先计算好这段延时需要等待的systick计数次数,这里为50x9(假设系统时钟为72MHz,那么systick每增加1,就是1/9us),然后我们就一直统计systick的计数变化,直到这个值变化了50x9,一旦检测到变化达到或者超过这个值,就说明延时50us时间到了。这样,我们只是抓取systick计数器的变化,并不需要修改systick的任何状态,完全不影响systick作为UCOS时钟节拍的功能,这就是实现delay和操作系统共用systick定时器的原理。

操作系统支持宏定义及相关函数
当需要delay_ms和delay_us支持操作系统(OS)的时候,我们需要用到3个宏定义和4个函数,宏定义及函数代码如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

delay_init函数

该函数用来初始化 2 个重要参数:fac_us 以及 fac_ms同时把 SysTick 的时钟源选择为外部时钟,如果需要支持操作系统(OS),只需要在 sys.h 里面,设置SYSTEM_SUPPORT_OS 宏的值为 1 即可然后,该函数会根据 delay_ostickspersec 宏的设置,来配置 SysTick 的中断时间,并开启 SysTick 中断。具体代码如下:
在这里插入图片描述
可以看到,delay_init 函数使用了条件编译,来选择不同的初始化过程,如果不使用 OS 的时候,只是设置一下 SysTick 的时钟源以及确定 fac_us 和 fac_ms 的值。而如果使用 OS 的时候,则会进行一些不同的配置,这里的条件编译是根据SYSTEM_SUPPORT_OS 这个宏来确定的,该宏在 sys.h 里面定义

SysTick 是 MDK 定义了的一个结构体(在 core_m3.h 里面),里面包含 CTRL、LOAD、VAL、CALIB 等 4 个寄存器,
SysTick->CTRL 的各位定义如图 5.1.2.1 所示:
在这里插入图片描述
在这里插入图片描述

delay_us函数
该函数用来延时指定的us,其参数nus为要延时的微秒数。该函数有使用OS和不使用OS两个版本,这里我们分别介绍,首先是不使用OS的时候,实现函数如下:
在这里插入图片描述
有了上面对 SysTick 寄存器的描述,这段代码不难理解。其实就是先把要延时的 us 数换算成 SysTick 的时钟数,然后写入 LOAD 寄存器。然后清空当前寄存器 VAL 的内容,再开启倒数功能等到倒数结束,即延时了 nus。最后关闭 SysTick,清空 VAL 的值。实现一次延时 nus的操作,但是这里要注意 nus 的值,不能太大,必须保证 nus<=(2^24)/fac_us,否则将导致延时时间不准确。这里特别说明一下:temp&0x01,这一句是用来判断 systick 定时器是否还处于开启状态,可以防止 systick 被意外关闭导致的死循环。

再来看看使用OS的时候,delay_us的实现函数如下:
在这里插入图片描述
这里就正是利用了我们前面提到的时钟摘取法ticks 是延时 nus 需要等待的 SysTick 计数次数(也就是延时时间),told 用于记录最近一次的 SysTick->VAL 值,然后 tnow 则是当前的SysTick->VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 tcnt 里面,然后通过对比 tcnt 和 ticks,来判断延时是否到达,从而达到不修改 SysTick 实现 nus 的延时,从而可以和 OS 共用一个 SysTick

上面的 delay_osschedlock 和 delay_osschedunlock 是 OS 提供的两个函数,用于调度上锁和解锁,这里为了防止 OS 在 delay_us 的时候打断延时,可能导致的延时不准,所以我们利用这两个函数来实现免打断,从而保证延时精度!同时,此时的 delay_us,可以实现最长 2^32us 的延时,大概是 4294 秒。

在这里插入图片描述
此部分代码和 5.1.3 节的 delay_us(非 OS 版本)大致一样,但是要注意因为 LOAD 仅仅是一个 24bit 的寄存器,延时的 ms 数不能太长。否则超出了 LOAD 的范围,高位会被舍去,导致延时不准。最大延迟 ms 数可以通过公式:nms<=0xffffff81000/SYSCLK 计算。SYSCLK 单位为 Hz,nms 的单位为 ms。如果时钟为 72M,那么 nms 的最大值为 1864ms。超过这个值,建议通过多次调用 delay_ms 实现,否则就会导致延时不准确。

再来看看使用OS的时候,delay_us的实现函数如下:
在这里插入图片描述
在这里插入图片描述

sys文件夹介绍

sys文件夹包含了sys.c和sys.h两个文件。在sys.h里面定义了STM32的IO口输入读取宏定义和输出宏定义。sys.c里面只定义了一个中断分组函数。

IO口的位操作实现

该部分代码在 sys.h 文件中,实现对 STM32 各个 IO 口的位操作,包括读入和输出。当然在这些函数调用之前,必须先进行 IO 口时钟的使能和 IO 口功能定义。此部分仅仅对 IO 口进行输入输出读取和控制。
位带操作简单的说,就是把每个比特膨胀为一个 32 位的字,当访问这些字的时候就达到了访问比特的目的,比如说 BSRR 寄存器有 32 个位,那么可以映射到 32 个地址上,我们去访问这 32 个地址就达到访问 32 个比特的目的。这样我们往某个地址写 1 就达到往对应比特位写 1的目的,同样往某个地址写 0 就达到往对应的比特位写 0 的目的。
在这里插入图片描述
对于上图,我们往address0地址写1,那么就可以达到往寄存器的第0位Bit0赋值1的目的。这里我们不想讲的过于复杂,因为位带操作在实际开发中可能只是用来IO口的输入输出还比较方便,其他操作在日常开发中也基本很少用。下面我们看看sys.h中位带操作的定义:
在这里插入图片描述
在这里插入图片描述

usart文件夹介绍

usart 文件夹内包含了 usart.c 和 usart.h 两个文件。这两个文件用于串口的初始化和中断接收。这里只是针对串口 1,比如你要用串口 2 或者其他的串口,只要对代码稍作修改就可以了。usart.c里面包含了2个函数一个是void USART1_IRQHandler(void);另外一个是void uart_init(u32 bound);里面还有一段对串口 printf 的支持代码,如果去掉,则会导致 printf 无法使用,虽然软件编译不会报错,但是硬件上 STM32 是无法启动的,这段代码不要去修改。

printf函数支持

在这里插入图片描述
在这里插入图片描述

uart_init函数

void uart_init(u32 pclk2,u32 bound)函数是串口1初始化函数。该函数有1个参数为波特率。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

USART1_IRQHandler函数

void USART1_IRQHandler(void)函数是串口 1 的中断响应函数当串口 1 发生了相应的中断后,就会跳到该函数执行。中断相应函数的名字是不能随便定义的,一般我们都遵循 MDK定义的函数名。这些函数名字在启动文件 startup_stm32f10x_hd.s 文件中可以找到。
函数体里面通过函数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
EN_USART1_RX 和 USART_REC_LEN 都是在 usart.h 文件里面定义的,当需要使用串口接收的时候,我们只要在 usart.h 里面设置 EN_USART1_RX 为 1 就可以了不使用的时候,设置,EN_USART1_RX 为 0 即可,这样可以省出部分 sram 和 flash,我们默认是设置 EN_USART1_RX为 1,也就是开启串口接收的

SYSTEM_SUPPORT_OS,则是用来判断是否使用 OS,如果使用了 OS,则调用 OSIntEnter和 OSIntExit 函数,如果没有使用 ucos,则不调用这两个函数(这两个函数用于实现中断嵌套处理,这里我们先不理会)。

发布了34 篇原创文章 · 获赞 1 · 访问量 1430

猜你喜欢

转载自blog.csdn.net/qq_38958704/article/details/105194707