蓝桥杯嵌入式省赛冲刺11天——滴答定时器和DMA串口
1
以B站up:Orion_瑞恩教程为来源,本帖记录我自己敲出来的代码和心得体会
1.滴答定时器:使用时将中断优先级调到0最高
来源
stm32g4xx_it.c 中有void SysTick_Handler(void),进入声明,看到HAL_IncTick();,再进入,uwTick += uwTickFreq; 意义在下
* @brief 调用此函数可对作为应用程序时基的全局变量 “uwTick” 进行递增操作。加到溢出会赋0.
* @note 在默认实现中,系统滴答定时器(SysTick)的中断服务程序(ISR)每隔 1 毫秒对该变量执行一次递增操作。
* @note 此函数被声明为 __weak 类型,这样在用户文件中有其他实现时,该函数可被覆盖。
只调用全局变量uwTick代码
void main_pros(void)
{
led_show(1,led_show_flag);
data_pros();
}
uint32_t uwTick_last;//上一次uwTick的值
void data_pros(void)
{
if(uwTick - uwTick_last > 500)
{led_show_flag ^=1;
uwTick_last = uwTick;
}
}
- 功能:该函数用于定时切换
led_show_flag的值,切换周期约为 500 毫秒=0.5s,控制led1以0.5s为周期闪烁。 - 详细解释:
uwTick通常是 HAL 库中的系统滴答计数器,它会随着时间不断递增。uwTick_last记录上一次执行操作时的uwTick值。if(uwTick - uwTick_last > 500):检查当前uwTick值与uwTick_last的差值是否大于 500。如果大于 500,说明距离上一次操作已经过去了至少 500 毫秒。led_show_flag ^= 1;:使用异或运算符^将led_show_flag的值取反。这样led_show_flag会在 0 和 1 之间交替变化。uwTick_last = uwTick;:更新uwTick_last的值为当前的uwTick值,以便下一次计时。
调用SysTick_Handler代码
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
剪贴到fun.c中,注意是最后一次配置MX后再粘贴,否则MX会生成SysTick_Handler
stm32g4xx_it.c中编程
使用全局变量
2.DMA串口
1.MX,Connectivity,USART1,mode选asynchronous异步通信
Baud Rate 115200
2.改GPIO口,PA9,PA10 USART
3.NVIC 中断使能,DMA Settings usart1_rx
来源
stm32g4xx_hal_uart.h中找回调函数(与不用DMA不同)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);
stm32g4xx_hal_uart_ex.h找接收函数(与不用DMA不同)
HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
//“the process goes to idle”(该进程进入空闲状态)
Ex 通常代表 “Extended”,也就是 “扩展” 的意思,Ex 表明该函数属于 UART(通用异步收发传输器)功能的扩展版本。
ToIdle 指的是 “直到空闲状态”,此函数的作用是借助 DMA(直接内存访问)方式接收 UART 数据,直至检测到空闲状态。当检测到 USART1 的空闲状态时,会调用 HAL_UARTEx_RxEventCallback 函数,你可以在这个函数里处理接收到的数据。处理完成后,再次调用 HAL_UARTEx_ReceiveToIdle_DMA 函数,重新启动接收过程。
优点:一次性接受完数据,并且接受完成后再进入中断,再讲flag置1,从而省掉定时器。代码的逻辑是一次性接收完一帧数据,接收完成(检测到空闲)后进入中断回调函数,在回调函数中将 uart_flag 置为 1,然后重新启动 DMA 接收下一帧数据。
为什么发送和接受采用两种数据类型?
uart_send使用char类型:char类型在 C 语言里常常用于表示字符数据。当你需要通过 UART 发送文本信息或者字符序列时,使用char类型会让代码的语义更加清晰。例如,你要发送一个字符串"Hello, World!",用char数组来存储是非常自然的,因为字符串在 C 语言里本身就是以char数组的形式存在。uart_accept使用uint8_t类型:uint8_t是无符号 8 位整数类型,它能表示的范围是 0 - 255。在接收数据时,数据可能并非是字符,而是二进制数据,像传感器采集到的原始数据、图片数据等。使用uint8_t类型可以更准确地表示和处理这些二进制数据,避免了字符编码带来的潜在问题。
uart_accept[30]={'\0'};接受7位(123-111),30大小也不变
char uart_send[30]={'\0'};//发送的数据
uint8_t uart_accept[30]={'\0'};//接受的数据
uint8_t uart_flag;
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart->Instance == USART1)
{
uart_flag=1;
HAL_UARTEx_ReceiveToIdle_DMA(huart, uart_accept, 30);
}
}
extern uint8_t uart_accept[30];//在fun.h声明
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uart_accept, 30);//使能,main.c
while(1)
void uart_pros(void)
{
if(uart_flag)
{
if(uart_accept[0]=='B'&&uart_accept[1]=='1'&&strlen((char*)uart_accept)==2)
{
sprintf(uart_send,"OK!\r\n");
HAL_UART_Transmit(&huart1, (uint8_t*)uart_send, strlen(uart_send), 50);
}
else
{
sprintf(uart_send,"ERROR!\r\n");
HAL_UART_Transmit(&huart1, (uint8_t*)uart_send, strlen(uart_send), 50);
}
uart_flag=0;
memset(uart_accept,'\0',sizeof(uart_accept)); // 使用sizeof清空缓冲区更安全
}
}
strlen 是一个标准库函数,它的功能是计算字符串的实际长度,也就是从字符串起始位置开始,到第一个空字符 '\0' 结束所包含的字符个数,不包含空字符本身.
- 使用
strlen时,字符串必须以'\0'结尾,否则会导致未定义行为。
memset() 是 C 语言标准库 <string.h> 中提供的一个函数,会把从 s 开始的连续 n 个字节的内存区域都设置为 c 的低 8 位所代表的值。
void *memset(void *s, int c, size_t n);
memset(uart_accept,'\0',strlen((char*)uart_accept));
// 这行代码的主要目的是将 uart_accept 数组中已存储的字符串部分清零
strlen 函数会从传入的指针所指向的位置开始,逐个字符检查,直到遇到空字符 '\0' 为止,然后返回从起始位置到空字符之间的字符个数(不包含空字符本身)。
更多推荐



所有评论(0)