UART结构体定义
UART_HandleTypeDef huart1;
HAL_UART_Transmit();串口发送数据,使用超时管理机制
HAL_UART_Receive();串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收
HAL_UART_Transmit_DMA();串口DMA模式发送
HAL_UART_Transmit_DMA();串口DMA模式接收
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();串口接收错误函数
注意:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改。
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
HAL_UART_GetState(); 判断UART的接收是否结束,或者发送数据是否忙碌
由于笔者上一篇博客已经讲述了中断的原理故而这里不再赘述,https://blog.csdn.net/weixin_64559251/article/details/127455380?spm=1001.2014.3001.5501
采用串口中断方式重做上周的串口通信作业,分别实现:1)当stm32接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”(提示:采用一个全局标量做信号灯);2)当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数。
(1)在STMCubeMX主界面,创建新项目,点击ACCEE TO MCU SELECTOR
(2)在part name
里选择自己的芯片(一般选择直接搜索所需芯片),本文采用STM32F103C8T6
点击信息栏中的具体芯片信息选中,点击start project
(3)设置RRC,设置高速外部时钟HSE,选择外部时钟源
(4)设置串口
1)点击USART1
2)设置MODE
为异步通信
3)基础参数:波特率为115200 Bits/s
。传输数据长度为8 Bit
。奇偶检验无,停止位1,接收和发送都使能
4)GPIO引脚设置 USART1_RX/USART_TX
(这里一般自动设置好了)
5) NVIC Settings 一栏使能接收中断
可发现引脚已自动配置如下图
(5)时钟设置
在main.c和usart.c中添加头文件#include "stdio.h"
(1)在usart.c文件中,添加如下代码,进行重定义
/* USER CODE BEGIN 1 */
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#if 1
//#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x0001);
return ch;
}
#endif
/* USER CODE END 1 */
(2)main.c主函数中,添加发送数据
while (1)
{
/* USER CODE END WHILE */
if(flag==1)
{
printf("Hello windows!\r\n");
HAL_Delay(500);
}
/* USER CODE BEGIN 3 */
}
(3)在main.c中添加如下定义,用来接收串口数据
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_RxBuff[256]; //接收缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t str1[20] = "stop stm32";
uint8_t str2[20] = "go stm32"; //定义需要识别的字符串
uint8_t cAlmStr[] = "数据溢出(大于256)\r\n";
int flag=1;
(4)添加开启接收中断的语句
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
/* USER CODE END 2 */
(5)在main.c下部添加中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback could be implemented in the user file
*/
/*if(Uart1_RxBuff[0]=='s')
{
flag=0;
}
if(Uart1_RxBuff[0]=='t')
{
flag=1;
}
*/
if (strcmp(Uart1_RxBuff, str1) == 0) flag = 0;
if (strcmp(Uart1_RxBuff, str2) == 0) flag = 1;
if(Uart1_Rx_Cnt >= 255) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
串口通信(1)
串口通信(2)
DMA,全称Direct MemoryAccess,即直接存储器访问
CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?
因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B 不经过CPU的处理。
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:
外设到内存
内存到外设
内存到内存
外设到外设
普通模式
传输结束后(即要传输数据的数量达到零),将不再产生DMA操作。若开始新的DMA传输,需在关闭DMA通道情况下,重新启动DMA传输。
循环模式
可用于处理环形缓冲区和连续数据流(例如ADC扫描模式)。当激活循环模式后,每轮传输结束时,要传输的数据数量将自动用设置的初始值进行加载, 并继续响应DMA请求。
(5)分别点击两个通道,查看模式设置是否为Normal,右侧Memory是否选中
(6)在System view下选择DMA,并ADD通道MEMTOMEM
(1)在main.c文件添加代码
uint8_t Senbuff[] = "Hello world!\r\n"; //定义数据发送数组
(2)在main.c文件添加代码
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
HAL_Delay(1000);
至此笔者已经将串口通信的三种方式分别演示了一遍,轮询,中断以及DMA。在此次实验中笔者更为深入地了解到了串口通信,也发现了DMA的妙用,DMA传输过程不占用CPU资源,可以边传输边运行其他任务,更加高效。在今后我们也可以多加练习,多加运用,对这些内容进行更为深入的体会,笔者也会继续努力。
1.https://blog.csdn.net/qq_46467126/article/details/121076618?spm=1001.2014.3001.5502
2.https://blog.csdn.net/qq_43279579/article/details/110138564
3.https://blog.csdn.net/as480133937/article/details/104827639/