文章目录
- sys文件夹
- delay文件夹
-
- usart文件夹
- printf函数输出流程
- printf函数的使用
- printf函数支持
-
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(当前数值寄存器)
函数介绍
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS
uint32_t reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
g_fac_us = sysclk;
#if SYS_SUPPORT_OS
reload = sysclk;
reload *= 1000000 / delay_ostickspersec;
g_fac_ms = 1000 / delay_ostickspersec;
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
SysTick->LOAD = reload;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
#endif
}
delay_init 函数使用了条件编译,来选择不同的初始化过程,如果不使用 OS 的时候,只是设置一下 SysTick 的时钟源以及确定 fac_us 值。而如果使用 OS 的时候,则会进行一些不同的配置,这里的条件编译是根据 SYS_SUPPORT_OS 这个宏来确定的,该宏在 sys.h 里面定义。
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
ticks = nus * g_fac_us;
told = SysTick->VAL;
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow;
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
}
void delay_ms(uint16_t nms)
{
uint32_t repeat = nms / 540;
uint32_t remain = nms % 540;
while (repeat)
{
delay_us(540 * 1000);
repeat--;
}
if (remain)
{
delay_us(remain * 1000);
}
}
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
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 文件可以这样重新定义该函数:
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)
__asm(".global __use_no_semihosting\n\t");
__asm(".global __ARM_use_no_argv \n\t");
#else
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
#endif
使用的上面的代码,Keil 的编译器就不会把标准库的这部分函数链接到我们的代码里。如果用到原来半主机模式下的调试函数,需要重新实现它的一些依赖函数接口,对于 printf 函数需要实现的接口,我们的代码中将它们实现如下:
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
FILE __stdout;
fputc 的重定向和之前一样,重定向到串口 1 即可,如果硬件资源允许,读者有特殊需求,也可以重定向到 LCD 或者其它串口。
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0);
USART_UX->DR = (uint8_t)ch;
return ch;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)