蓝桥杯CT117E_M4(G431)-USART+DMA

1、功能实现及思路

功能:串口控制LED灯

思路:在串口接收回调函数中存储收到的数据,并根据\r\n来判断数据是否接收完整,然后根据相应指令开关灯

2、STM32CubeMX配置

2.1、USART1配置

串口模式选择异步通讯

DMA设置通过下面的Add添加两个DMA通道

2.2、时钟配置

配置系统时钟为80MHz

3、相关函数

3.1、HAL_UART_Receive_IT()

  • 函数功能:用于开启串口接收(需要注意每次接收完成后需要重新调用开启接收
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  • huart:UART句柄

  • pData:指向数据缓冲区(u8或u16数据元素)的指针(即你自己定义的数组或者变量)

  • Size:要接收的数据元素(u8或u16)的数量

只有当接收到Size个数据后才会进入回调函数HAL_UART_RxCpltCallback()

深入分析:该函数会将数据缓冲区的地址存储在UART句柄中的pRxBuffPtr变量中,当有数据进入后,其实还是跟标准库一样,会首先进入USART1_IRQHandler()这个中断服务函数,然后在中断服务函数中在调用HAL库的中断服务函数HAL_UART_IRQHandler(&huart1),然后在这个函数中去读取收到的数据并通过pRxBuffPtr地址往我们指定的数据缓冲区(即数组)里写入数据,并且在最后才会进入回到函数 HAL_UART_RxCpltCallback() 中,因此在HAL库开发中不像在标准库需要我们自己去读取寄存器的值并存储下来,而是可以直接在回调函数中去处理缓冲区的值。

3.2、HAL_UART_Receive_DMA()

  • 函数功能:用于开启DMA串口接收(需要注意每次接收完成后需要重新调用开启接收
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  • huart: UART句柄

  • pData: 指向数据缓冲区(u8或u16数据元素)的指针(即你自己定义的数组或者变量)

  • Size: 要接收的数据元素(u8或u16)的数量

这个函数与上面3.1的函数区别在于,该函数通过DMA进行串口数据传输和保存,当串口传输的数据量较大时,通过DMA来保存接收到的数据可以释放CPU资源以执行其他任务,通过这个函数开启接收不会进入串口中断服务函数,这是与上面3.1函数最大的区别,但是最后都会进入回调函数HAL_UART_RxCpltCallback() 中进行数据处理。

3.3、HAL_UART_Transmit_IT()

  • 函数功能:串口发送函数
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

参数与上面接收一致,只不过把接收变成了发送

3.4、HAL_UART_Transmit_DMA()

  • 函数功能:将要发送的数据通过DMA传给串口进行发送
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

3.5、HAL_UART_RxCpltCallback()

  • 接收完成回调函数

  • 函数功能:串口接收回调函数,当数据接收完成后会进入该回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

该函数需要重写,并再函数中对收到的数据进行分析处理

3.6、HAL_UART_RxHalfCpltCallback()

  • 半接收完成回调函数
  • 函数功能:当UART接收到一个字节的数据时,该函数会被HAL库自动调用,用于处理接收到一半数据包的情况
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)

与上面函数不同在于,只要有数据进来就会进入该回调函数,而3.1则在数据接收完成才会进入

举个栗子:

int main()
{
	HAL_UART_Receive_DMA(&huart1,&Rx_Buffer[0],2);	//开启串口接收
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		/*	点亮PC8	*/
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
		HAL_UART_Receive_DMA(&huart1,&Rx_Buffer[0],2); //继续接收
	}
}
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		/*	点亮PC15	*/
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
}

在该栗子中,当通过串口发送一个数据时会点亮PC15,只有发送了两个数据才会点亮PC8,因为在开启接收函数中设置了要接收的数据长度为2

4、编写回调函数

在通过STM32CubeMX生成代码之后,我们只需要在代码中重写回调函数即可,然后通过串口给单片机发送"LED1 OFF"即可点亮LED1,注意串口助手需要勾选发送新行,即每次发送数据后面都会跟\r\n,通过判断接收\r\n来表示数据是否发送完成

uint8_t Rx_Buffer[200];
uint8_t Rx,Rx_Length=0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		if(Rx == '\n')
		{
			Rx_Buffer[Rx_Length-1] = 0;//把保存的\r换行符去掉
			HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
			if(strcmp((char*)Rx_Buffer,"LED1 OFF")==0)
			{
				HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
				HAL_UART_Transmit_DMA(&huart1,(uint8_t*)"LED1已打开",strlen("LED1已打开"));
			}
			else if(strcmp((char*)Rx_Buffer,"LED1 ON")==0)
			{
				HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
				HAL_UART_Transmit_DMA(&huart1,(uint8_t*)"LED1已关闭",strlen("LED1已关闭"));
			}
			if(strcmp((char*)Rx_Buffer,"LED2 OFF")==0)
				HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET);
			else if(strcmp((char*)Rx_Buffer,"LED2 ON")==0)
				HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
			memset(Rx_Buffer,0,Rx_Length);
			Rx_Length = 0;
		}
		else
			Rx_Buffer[Rx_Length++] = Rx;
		HAL_UART_Receive_DMA(&huart1,&Rx,1);
	}
}

主函数中只需要启动第一次接收即可

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_DMA(&huart1,&Rx,1);//开启串口接收
  while (1)
  {
  }
}

串口助手


特别的

1、strcmp()

#include <stdio.h>
#include <string.h>
int main() {
	unsigned char p[20] = "hello 1";
	p[5] = 0;
	if (strcmp("hello", (char *)p)==0)
		printf("相等");
	else
		printf("不相等");
}

判断字符串p和“hello”是否相等,strcmp会比较str1和str2每一个字符,当出现字符不同时比较大小,str1字符比str2大则返回一个大于0的数,小则返回一个小于0的数

结果为相等,当字符串出现有字符为0则判断比较结束,两个字符串相等

2、memset()

#include <stdio.h>
#include <string.h>
int main() {
	unsigned char p[5];
	unsigned int t[5];
	memset(p, 1, 5);
	memset(t, 1, 5);
	printf("%d\n", p[0]);
	printf("%d\n", t[0]);
	printf("%x", t[0]);
}

void *memset(void *str, int c, size_t n)

通过memset设置数组数值,memset用于给指定数组设置某个c值,并可以选择设置要修改多少个值,需要注意的是memset的修改规则是按字节进行修改,即将数组中的每一个字节都修改成设定的c值,因此当对大于1个字节的数组进行修改时需要注意

结果为:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值