参考:https://blog.csdn.net/chenjk10/article/details/106264940
1.配置系统下载程序方式
2.RCC外部晶振
3.串口声明
4.系统时钟
5.生成文件根据自己需求配置
我这设置了所有库,可以选第二个仅需求的
还加了其他功能,这里可不参考,DMA要放在串口前
6.生成后修改
6.1 usart.h
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
#define USART1_DMA_REC_SIE 256
#define USART1_REC_SIE 512
#define USART_DMA_SENDING 1//DMA发送
#define USART_DMA_SENDOVER 0//DMA发送结束
typedef struct
{
uint8_t UsartRecFlag:1;//接收标志
uint8_t dmaSend_flag;//发送标志
uint16_t UsartDMAReclen;//DMA接收长度
uint16_t UsartReclen;//串口数据缓存长度
uint8_t Usart1DMARecBuffer[USART1_DMA_REC_SIE];//DMAbuffer缓存数据
uint8_t Usart1RecBuffer [USART1_REC_SIE] ;//串口1缓存数据
}teUsart1type;
extern teUsart1type Usart1type;//定义数据
void UsartReceive_IDLE(UART_HandleTypeDef *huart);
void Usart1SendData_DMA1(uint8_t *pdata, uint16_t Length);
void Analysis_Serial_Data(void);
void EnableUsart_IT(void);
/* USER CODE END Private defines */
6.2 usart.c,接收为双缓存方式,一般DMA接收放在,DMArec里,若不及时处理,下次接收会丢失,放入usartrec里,可避免,实现处理多少拿多少,dma接收的数据根据处理情况放入相应位置。
teUsart1type Usart1type;
/*****************************************************************************
* 函 数 名 : EnableUsart_IT
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 : 串口2初始化函数,空闲与DMA中断
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
*****************************************************************************/
void EnableUsart_IT(void)
{
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); // 开启串口2 空闲接收中断
HAL_UART_Receive_DMA(&huart2, Usart1type.Usart1DMARecBuffer, USART1_DMA_REC_SIE);//DMA中断
}
/*****************************************************************************
* 函 数 名 : Usart1SendData_DMA
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 : 串口2DMA发送函数
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
*****************************************************************************/
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
{
//while(Usart1type.dmaSend_flag == USART_DMA_SENDING);
Usart1type.dmaSend_flag = USART_DMA_SENDING;
HAL_UART_Transmit_DMA(&huart2, pdata, Length);
}
/*****************************************************************************
* 函 数 名 : HAL_UART_TxCpltCallback
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 : 串口2DMA发送回调函数
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
*****************************************************************************/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_DMA_DISABLE(huart->hdmatx);
if(huart->Instance == huart2.Instance)
Usart1type.dmaSend_flag = USART_DMA_SENDOVER;
}
/*****************************************************************************
* 函 数 名 : UsartReceive_IDLE
* 负 责 人 : by
* 创建日期 : 2020年8月27日
* 函数功能 : 串口接受空闲中断处理函数
* 输入参数 : UART_HandleTypeDef *huart 串口句柄
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :DMA接收的数据缓存在USART接收中,释放DMA继续接收
* 其 它 :
*****************************************************************************/
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
{
if(huart->Instance == huart2.Instance)//判断是否为串口2
{
if(RESET != __HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) //是否为空闲中断
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除空闲中断
HAL_UART_DMAStop(&huart2); //停止本次DMA传输
Usart1type.UsartDMAReclen = USART1_DMA_REC_SIE - huart2.hdmarx->Instance->NDTR; //计算本次传输数据长度
if(Usart1type.UsartReclen>0)
{
memcpy(&Usart1type.Usart1RecBuffer[Usart1type.UsartReclen], Usart1type.Usart1DMARecBuffer, Usart1type.UsartDMAReclen);
Usart1type.UsartReclen+=Usart1type.UsartDMAReclen;
}
else if(Usart1type.UsartReclen>USART1_REC_SIE)//如果串口接收数据溢出,从0开始记录
{
Usart1type.UsartReclen=0;
}
else
{
memcpy(Usart1type.Usart1RecBuffer, Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMAReclen);
Usart1type.UsartReclen=Usart1type.UsartDMAReclen;
}
memset(Usart1type.Usart1DMARecBuffer, 0x00, sizeof(Usart1type.Usart1DMARecBuffer ));//DMA数据清零
Usart1type.UsartRecFlag=1;//空闲标志位置1
HAL_UART_Receive_DMA(&huart2, Usart1type.Usart1DMARecBuffer, USART1_DMA_REC_SIE);//重新打开DMA
}
}
}
}
/*****************************************************************************
* 函 数 名 : Analysis_Serial_Data
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 : 串口接收数据直接打印
* 输入参数 : void 无
* 输出参数 : 无
* 返 回 值 : void
* 调用关系 :
* 其 它 :
*****************************************************************************/
void Analysis_Serial_Data(void)
{
if(Usart1type.UsartRecFlag)
{
Usart1SendData_DMA(Usart1type.Usart1RecBuffer,Usart1type.UsartReclen);
Usart1type.UsartRecFlag=0;
Usart1type.UsartReclen=0;//测试处理为将全部数据进行打印处理,若需解析,可将解析长度进行减少,多次处理
}
}
/*****************************************************************************
* 函 数 名 : fputc
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 : 串口2 printf重定义
* 输入参数 : printf(d%/s%/%c,a)
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
*****************************************************************************/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2,(uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 1 */
DMA发送函数,网上给的教程while是用来保证DMA收发节奏的,节奏错误会出现死循环情况,给注释掉了。。。。
//while(Usart1type.dmaSend_flag == USART_DMA_SENDING);
Usart1type.dmaSend_flag = USART_DMA_SENDING;
6.3 主函数,只用DMA通讯只需添加EnableUsart_IT();/*串口初始化*/,即可,对应usart.c里的函数
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);/*使能定时器3中断***/
HAL_TIM_Base_Start_IT(&htim2);/*使能定时器2中断*/
HAL_CAN_Start(&hcan1);/*can初始化*/
HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
EnableUsart_IT();/*串口初始化*/
CAN_User_Init(&hcan1);
/* USER CODE END 2 */
循环,对应数据输出
/* USER CODE BEGIN WHILE */
while (1)
{
Analysis_Serial_Data();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
7、实验结果,间隔10ms,连续发送,未出现丢帧,或其他错误