注意:利用HAL库的 HAL_UARTEx_ReceiveToIdle_DMA()函数,代码比较简洁, 如果编辑器找不到函数HAL_UARTEx_ReceiveToIdle_DMA(),需要更新HAL库至最新版本。
一、串口及DMA的CubeMx配置
打开串口接收DMA,模式选择Normal,然后再回调函数HAL_UARTEx_RxEventCallback中重新调用HAL_UARTEx_ReceiveToIdle_DMA函数即可实现不断接收不定长数据的效果。
配置波特率:
注意要打开串口的全局中断,把相应的DMA中断关闭,对于不定长数据,DMA中断判别不了到底要接收几个字节的数据才中断,而串口可以,否则容易引起异常。如果是定长的数据的话,可以关闭串口中断,直接开DMA中断。
点击侧边栏的NVIC选项,取消选中 Force DMA channels Interrupts,这样DMA就可以自定义DMA中断优先级了。
配置时钟树:
然后即可生成代码。
程序部分:
#include "stdio.h"
#include "string.h"
#include <stdarg.h>
#include "usart.h"
#include "dma.h"
uint8_t rx_buffer[BUF_SIZE]; // 创建接收缓存,大小为BUF_SIZE
#define BUF_SIZE 200
extern uint8_t rx_buffer[BUF_SIZE]; // 创建接收缓存,大小为BUF_SIZE
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUF_SIZE);
这里也加入这个函数。
最后调用事件回调函数HAL_UARTEx_RxEventCallback();
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
Size = BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff); //将接受到的数据再发回上位机
memset(rx_buffer, 0, Size);
//放在void USART1_IRQHandler(void)函数里,不要放在这
//HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUF_SIZE);不要放在这里,如果放在这里,不仅上位机的波特率改变不能正常接收,就算改回去了也会接收不了
}
}
测试:匿名助手及其他串口助手
增强代码移植性:
main.h:
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
//Ex_NVIC_Config专用定义
#define GPIO_A 0
#define GPIO_B 1
#define GPIO_C 2
#define GPIO_D 3
#define GPIO_E 4
#define GPIO_F 5
#define GPIO_G 6
#define FTIR 1 //下降沿触发
#define RTIR 2 //上升沿触发
//JTAG模式设置定义
#define JTAG_SWD_DISABLE 0X02
#define SWD_ENABLE 0X01
#define JTAG_SWD_ENABLE 0X00
串口重定向paintf
记得.h文件声明函数。
void my_printf(int ch,uint8_t *format, ...)
{
char buf[512]; //定义临时数组,根据实际发送大小微调
va_list args;
va_start(args, format);
uint16_t len = vsnprintf((char *)buf, sizeof(buf), (char *)format, args);
va_end(args);
if(ch == 1)
HAL_UART_Transmit(&huart1,(uint8_t *)buf,len,1000);
// else if(ch == 2)
// HAL_UART_Transmit(&huart2,(uint8_t *)buf,len,1000);
// else if(ch == 3)
// HAL_UART_Transmit(&huart3,(uint8_t *)buf,len,1000);
}
void u1_printf(uint8_t *format)
{
my_printf(1,format);
}
//void u2_printf(char *format)
//{
// my_printf(2,format);
//}
//void u3_printf(char *format)
//{
// my_printf(3,format);
//}
这样,可以直接用u1_printf发送了,那么下面可以修改为: