暑假开始的时候利用野火给的例程跑了一下微秒级延时函数,现在要用到但是懒得翻以前的程序,就想自己在Cube生成文件后自己写一个,然后果然忘了,有些函数找不到了,在查阅许多资料后终于写出来了,在这里做个记录,以后再忘可以翻自己的文章。
Cube生成代码
基础配置
这里用的F429,需要设置的有RCC调成外部晶振时钟
sys调整Debug
然后通用计时器、基本计时器随便选择一个即可(文章里看到好多人选的通用寄存器,但我试了一下基本计时器也可以)按照自己的时钟修改Prescaler的值即可
在配置时钟,时钟配多少问题不大,重要的是红框标注的两个值
这里分别是两个总线上提供给时钟的频率,后续的设置有关这个值,具体使用哪一个取决与使用的计时器占用的哪条线,可以在说明书上查找。
可以看到1、8-11用的APB2,因此这几个计时器的频率就是180MHZ,其余都是APB1,频率就是90MHZ
Prescaler和Period的设置
这两个的值设定取决于使用的单片机的时钟和需要多少时间间隔的时钟,Preacaler是预分频,这里虽然名字是分频,但实际作用确实计算系统时钟走多少个才是最小的一个时钟周期,例如我使用的TIM6的时钟频率是90MHZ,也就是(1/90M)即(1/90000000)是最小时钟周期
但是1us是(1/1000000),因此需要乘以90,才能得到1us,因此预分频这一项选择90-1(减1是因为系统里计数是从0开始的,在这里89就是90)
Period这个是指经过多少个时钟周期后触发一次中断,或者说时钟周期计数的上限是多少,本次只把这个计时器当一个微秒延时来使用,说实话只实现这个功能的情况下根本不需要设置中断,因此这个值设置没有太多讲究,具体的下面会说,涉及到中断的下次再写吧。
感觉上面讲的有点抽象,举一个形象点的例子,老师数数,数一个数就放一个学生回家,两个数之间的时间间隔由Prescaler决定,这里设置的是1us,就是每过1us放一个学生,然后老师一直数,最多数到Period,(注意这里一定要是数到!数过了不算!)老师数完之后触发一次中断,也就是说Period的值只跟中断相关(或许还有关其他功能但是没有接触到)。我们要设置Nus,就是让老师数n个数字,只要开启定时器,让他数n个,再关闭定时器,微秒级的延时就完成了。
为了能显示输出这里还加了一个串口,下面也会把串口转功能的代码发一下,挺好用的免得以后忘记了
配置很简单,也不需要中断
代码编写
搞定了初始化之后直接生成代码
红框的地方记得勾选一下,生成的代码比较好看
然后设置计时器,今天弄了半天都是在HAL库给的函数库里找函数,都没找到,后来才知道这个函数不显示在库内,也不知道为什么,具体怎么实现的也不是很清楚,但是看名字就知道用处
#include "timer.h"
#define timers htim6
void delay_us(uint16_t nus )
{
uint16_t delay=0xffff-nus-5;
HAL_TIM_Base_Start(&timers);
__HAL_TIM_SET_COUNTER(&timers,delay);
while(delay<0xffff-5)
{
delay=__HAL_TIM_GetCounter(&timers);
}
HAL_TIM_Base_Stop(&timers);
}
没错就是这个函数 __HAL_TIM_SET_COUNTER,这个是设定计时器从哪个数字开始的函数,这里delay初始值设置什么都可以,但要注意的是需要把Period的值设置大一点,经过实验这个period有个地方容易让人产生误解,他虽然是设置中断的,但是触发中断的形式不是说计数的值大于Period设置的值就中断,而需要恰好等于,这里将delay设置的值为0xffff-nus-5,就是说delay的值永远都在0xFFFF-nus-5到0xFFFF-5之间,同时Period设置的1,delay的值永远不会等于1,就是说永远也不会触发中断。
在实验过程中想要改变delay的初始值让他从0开始计数直到nus停止,这样比较符合逻辑,但是总是失败,原因也在此,因为此刻需要把Period的值调大一点,最好是0xFFFF,这样基本不会到上限。
修改后的代码如下,记得把Period的值拉到最高!!
#include "timer.h"
void delay_us(uint16_t nus)
{
uint16_t delay=0;
HAL_TIM_Base_Start(&timers);
__HAL_TIM_SET_COUNTER(&timers,delay);
while(delay<=nus)
{
delay=__HAL_TIM_GetCounter(&timers);
}
HAL_TIM_Base_Stop(&timers);
}
代码验证
随便写一点函数,这里反复运行1000000次1us的延时就是1s,串口输出1s效果比较明显,由于计时器内最高是65535,不能直接写1000000,因此写成10000,然后重复100次,最后可以在串口助手上得到1s间隔的输出,验证成功
其他
///重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口DEBUG_USART */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
///重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
int ch;
while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == RESET);
HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
最后再附加一个以前经常用的重定向串口的程序吧,把这一段复制到程序里行