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' 为止,然后返回从起始位置到空字符之间的字符个数(不包含空字符本身)。

更多推荐