STM32 第11讲 正点原子SYSTEM文件夹介绍及使用(sys/delay/usart)

SYSTEM 文件夹里面的代码由正点原子提供,STM32F4xx 系列的底层核心驱动函数,可以用在 STM32F4xx 系列的各个型号上面。SYSTEM 文件夹下包含了 delay、sys、usart 等三个文件夹。

sys文件夹

在这里插入图片描述

delay文件夹

函数简介

delay 文件夹内包含了 delay.c 和 delay.h 两个文件,包含7个函数。

void delay_osschedlock(void);
void delay_osschedunlock(void);
void delay_ostimedly(uint32_t ticks);
void SysTick_Handler(void);
void delay_init(uint16_t sysclk);
void delay_us(uint32_t nus);
void delay_ms(uint16_t nms);

前面 4 个函数,仅在支持操作系统(OS)的时候,需要用到,而后面 3 个函数,则不论是否支持 OS 都需要用到

Systick定时器

工作原理

CM4 内核处理器,内部包含了一个 SysTick 定时器,SysTick 是一个 24 位的向下递减的计数定时器,当计数值减到 0 时,将从 RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
在这里插入图片描述从上一讲的时钟树可以知道F4系列Systick的时钟源为HCLK即AHB总线上
重装载指范围0~16777215

寄存器

  • CTRL(控制及状态寄存器)
    在这里插入图片描述
    在ST公司产品中CLKSOURCE位被设置的功能是设置分频系数
  • LOAD(重装载数值寄存器)
    在这里插入图片描述
  • VAL(当前数值寄存器)
    在这里插入图片描述

函数介绍

  • delay_init()函数
/**
* @brief 初始化延迟函数
* @param sysclk: 系统时钟频率, 即 CPU 频率(rcc_c_ck), 168MHz
* @retval 无
*/ 
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS /* 如果需要支持 OS */
 uint32_t reload;
#endif
 /* SYSTICK 使用外部时钟源,频率为 HCLK */
 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 g_fac_us = sysclk; /* 不论是否使用 OS,g_fac_us 都需要使用 */
#if SYS_SUPPORT_OS /* 如果需要支持 OS. */
 reload = sysclk; /* 每秒钟的计数次数 单位为 M */
 /* 根据 delay_ostickspersec 设定溢出时间,reload 为 24 位
 寄存器,最大值:16777216,在 168M 下,约合 0.09986s 左右 */
 reload *= 1000000 / delay_ostickspersec; 
 g_fac_ms = 1000 / delay_ostickspersec; /* 代表 OS 可以延时的最少单位 */
 SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启 SYSTICK 中断 */
 SysTick->LOAD = reload; /* 每 1/delay_ostickspersec 秒中断一次 */
 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启 SYSTICK */
#endif
}

delay_init 函数使用了条件编译,来选择不同的初始化过程,如果不使用 OS 的时候,只是设置一下 SysTick 的时钟源以及确定 fac_us 值。而如果使用 OS 的时候,则会进行一些不同的配置,这里的条件编译是根据 SYS_SUPPORT_OS 这个宏来确定的,该宏在 sys.h 里面定义。

  • delay_init()函数
/**
* @brief 延时 nus
* @param nus: 要延时的 us 数.
* @note nus 取值范围 : 0 ~ 190887435us(最大值即 2^32/g_fac_us @g_fac_us = 21)
* @retval 无
*/
void delay_us(uint32_t nus)
{
 uint32_t ticks;
 uint32_t told, tnow, tcnt = 0;
 uint32_t reload = SysTick->LOAD; /* LOAD 的值 */
 ticks = nus * g_fac_us; /* 需要的节拍数 */
 told = SysTick->VAL; /* 刚进入时的计数器值 */
 while (1)
 {
 tnow = SysTick->VAL;
 if (tnow != told)
 {
 if (tnow < told)
 {
 tcnt += told - tnow; /* 这里注意一下 SYSTICK 是一个递减的计数器就可以了*/
 }
 else
 {
 tcnt += reload - tnow + told;
 }
 told = tnow;
 if (tcnt >= ticks)
 {
 break; /* 时间超过/等于要延迟的时间,则退出 */
 }
 }
 }
}
  • delay_ms函数
/**
* @brief 延时 nms
* @param nms: 要延时的 ms 数 (0< nms <= 65535)
* @retval 无
165
STM32F407 开发指南
正点原子探索者 STM32F407 开发板教程
*/
void delay_ms(uint16_t nms)
{
 uint32_t repeat = nms / 540; /* 这里用 540,是考虑到可能有超频应用*/
 uint32_t remain = nms % 540;
 while (repeat)
 {
 delay_us(540 * 1000); /* 利用 delay_us 实现 540ms 延时 */
 repeat--;
 }
 if (remain)
 {
 delay_us(remain * 1000); /* 利用 delay_us, 把尾数延时(remain ms)给做了 */
 }
}
  • HAL库的延时函数 HAL_Delay
__weak void HAL_Delay(uint32_t Delay)
{
 uint32_t tickstart = HAL_GetTick();
 uint32_t wait = Delay;
 /* Add a freq to guarantee minimum wait */
 if (wait < HAL_MAX_DELAY)
 {
 wait += (uint32_t)(uwTickFreq);
 }
 while ((HAL_GetTick() - tickstart) < wait)
 {
 }
}

HAL 库的延时函数有一个局限性,在中断服务函数中使用 HAL_Delay 会引起混乱(虽然一般禁止在中断中使用延时函数),因为它是通过中断方式实现,Systick 的中断优先级是最低的,所以在中断中运行 HAL_Delay 会导致延时出现严重误差。
HAL 库的 ms 级别的延时函数__weak void HAL_Delay(uint32_t Delay);它是弱定义函数,所以用户可以自己重新定义该函数。例如:我们在 deley.c 文件可以这样重新定义该函数:

/**
 * @brief HAL 库延时函数重定义
 * @param Delay 要延时的毫秒数
 * @retval None
 */
void HAL_Delay(uint32_t Delay)
{
 delay_ms(Delay);
}

usart文件夹

printf函数输出流程

在这里插入图片描述

printf函数的使用

printf("字符串\r\n");
printf("输出控制符",输出参数);
printf("输出控制符1输出控制符2…",输出参数1,输出参数2,…);
printf("非输出控制符 输出控制符 非输出控制符",输出参数);
printf("%% \r\n");
printf("\\\r\n");
printf("\"\"\r\n");

常见输出控制符
在这里插入图片描述
常见转义字符
在这里插入图片描述

printf函数支持

标准库下的 printf 为调试属性的函数,如果直接使用,会使单片机进入半主机模式(semihosting),这是一种调试模式,直接下载代码后出现程序无法运行,但是在连接调试器进行 Debug 时程序反而能正常工作的情况。
半主机是 ARM 目标的一种机制,用于将输入/输出请求从应用程序代码通信到运行调试器的主机。

在独立环境下运行调试功能的函数,我们这里是 printf,printf 对字符 ch 处理后写入文件 f,最后使用 fputc 将文件 f 输出到显示设备。对于 PC 端的设备,fputc 通过复杂的源码,最终把字符显示到屏幕上。那我们需要做的,就是把 printf 调用的 fputc 函数重新实现,重定向fputc 的输出,同时避免进入半主模式。

避免进入半主机模式的方法有两种:微库法 + 代码法

微库法

ARM 的 C 微库 MicroLib 是为嵌入式设备开发的一套类似于标准 C 接口函数的精简代码库,用于替代默认 C 库,是专门针对专业嵌入式应用开发而设计的,特别适合那些对存储空间有特别要求的嵌入式应用程序,这些程序一般不在操作系统下运行。
微库只提供分离的堆和栈两区存储模式等等,它裁减了很多函数,而且还
有很多东西不支持。如果原来用标准库可以跑,选择 MicroLib 后却突然不行了,是很常见的。

使用微库时只需要在Keil中勾选Use MicroLIB即可
在这里插入图片描述

代码法

即取消 ARM 的半主机工作模式;只需在代码中添加不使用半主机的声明即可。

#if (__ARMCC_VERSION >= 6010050) /* 使用 AC6 编译器时 */
__asm(".global __use_no_semihosting\n\t"); /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t"); /* AC6 下需要声明 main 函数为无参数格式,否
则部分例程可能出现半主机模式 */
#else
/* 使用 AC5 编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)
/* 解决 HAL 库使用时, 某些情况可能报错的 bug */
struct __FILE
{
 int handle;
 /* Whatever you require here. If the only file you are using is */
 /* standard output using printf() for debugging, no file handling */
 /* is required. */
};
#endif

使用的上面的代码,Keil 的编译器就不会把标准库的这部分函数链接到我们的代码里。如果用到原来半主机模式下的调试函数,需要重新实现它的一些依赖函数接口,对于 printf 函数需要实现的接口,我们的代码中将它们实现如下:

/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string 函数,以同时兼
容 AC6 和 AC5 模式 */
int _ttywrch(int ch)
{
 ch = ch;
 return ch;
}
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
 x = x;
}
char *_sys_command_string(char *cmd, int len)
{
 return NULL;
}
/* FILE 在 stdio.h 里面定义. */
FILE __stdout;

fputc 的重定向和之前一样,重定向到串口 1 即可,如果硬件资源允许,读者有特殊需求,也可以重定向到 LCD 或者其它串口。

/* 重定义 fputc 函数, printf 函数最终会通过调用 fputc 输出字符串到串口 */
int fputc(int ch, FILE *f)
{
 while ((USART1->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
 USART_UX->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到 DR 寄存器 */
 return ch;
}
  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32是一系列由意法半导体(STMicroelectronics)开发的32位微控制器。它们广泛应用于各种嵌入式系统中,具有高性能、低功耗和丰富的外设功能。 系统函数delay是一种用于延时的函数,用于在程序中生成一段时间的延迟。它可以使用微秒或毫秒作为延迟时间单位,并在特定的时间周期内产生精确的延迟效果。延时函数的精确性对于一些特殊的应用场景非常重要,例如需要控制精确时序的通信协议或需要精确频率和时间间隔的任务。 sys文件夹STM32标准外设库的一部分,包含了与系统相关的函数和定义,用于初始化和配置STM32微控制器的各种系统资源,如时钟、中断等。sys文件夹对于正确使用STM32系列微控制器提供了必要的支持。 USART文件夹sys文件夹的子文件夹之一,其中包含了与USART(通用同步异步收发器)外设相关的函数和定义。USART是一种通信接口,用于在微控制器与其他设备之间进行串行数据传输。USART文件夹中的函数可以帮助程序员初始化和配置USART外设,并实现数据的发送和接收。 要下载sysUSART文件夹,可以访问意法半导体的官方网站或其他相关资源网站,搜索相关的开发板或型号的标准外设库,通常可以找到相应的软件包或文档,其中包含了这些文件夹的完整代码和说明。开发板制造商也常常提供相关的软件包和文档,以便于开发者快速上手和使用
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值