PY32F003F18使用DMA串口,官方程序省FLASH,但不省内存。单片机内存够大,节省没意义,故做了修改,少用HAL库中的发送和接收,从里面抠出有用的部分,修修改改就可以了。
一、DMA串口初始化流程:
假定:串口发送引脚USART2_TX使用PA0,串口接收引脚USART2_RX使用PA1
1、使能USART2外设时钟,__HAL_RCC_USART2_CLK_ENABLE();
2、使能GPIOA时钟,__HAL_RCC_GPIOA_CLK_ENABLE();
3、将串口发送引脚USART2_TX映射到PA0
4、将串口接收引脚USART2_RX映射到PA1
5、设置串口帧格式和波特率
6、将DMA通道1映射到USART2_TX,将DMA通道2映射到USART2_RX
__HAL_RCC_SYSCFG_CLK_ENABLE();//使能RCC外设时钟
HAL_SYSCFG_DMA_Req(0x0807);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA1_MAP[4:0]=7,将DMA通道1映射到USART2_TX
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
7、配置DMA串口发送,传输"从内存到外设"
8、配置DMA串口接收,传输"从外设到内存"
9、关闭串口接收中断
10、不使能"串口发送数据寄存器为空"产生中断
11、不使能"串口发送完成"产生中断
二、测试程序
#include "USART2.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "My_DMA.h"
#include "delay.h"
//PA0 ------> USART2_TX
//PA1 ------> USART2_RX
uint8_t USART2_TX_Buffer[USART2_TX_Buffer_Size]; //USART2发送缓冲区数组;
uint8_t USART2_TX_Buffer_Send_Index=0; //USART2_TX_Buffer[]的发送索引值;
uint8_t USART2_TX_Buffer_Load_Index=0; //USART2_TX_Buffer[]的装载索引值
uint8_t USART2_TX_Completed_Flag;
uint8_t USART2_TX_Overtime_Conter;//USART2发送超时计数器
uint8_t USART2_RX_Buffer[USART2_RX_Buffer_Size]; //USART2接收缓冲区数组
uint8_t USART2_RX_Buffer_StartIndex;//USART2_RX_Buffer[]的装载索引值
uint8_t USART2_RX_Buffer_EndIndex;//USART2_RX_Buffer[]的装载索引值
uint8_t USART2_RX1_Buffer[USART2_RX1_Buffer_Size]; //USART2接收缓冲区数组
void USART2_Init(uint32_t baudrate);
void Usart2Send(void);
void Usart2Receive(void);
void Usart2Send_DMA1_CH1_config(void);
void Usart2Receive_DMA1_CH2_config(void);
#if USART2Type == 0
//函数功能:
//PA2是为USART2_TX,PA3是USART2_RX
//中断优先级为0x01
//波特率为115200,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,只允许接收中断,并使能串口
void USART2_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructureure;
UART_HandleTypeDef UART_HandleStructureure;
/HAL_UART_MspInit开始
__HAL_RCC_USART2_CLK_ENABLE();//使能USART2外设时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
//串口引脚映射开始/
GPIO_InitStructureure.Pin = GPIO_PIN_2; //选择第2脚,PA2是为USART2_TX
GPIO_InitStructureure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
GPIO_InitStructureure.Pull = GPIO_PULLUP; //引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
GPIO_InitStructureure.Alternate = GPIO_AF4_USART2; //将引脚复用为USART2
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//将PA0初始化为USART2_TX
GPIO_InitStructureure.Pin = GPIO_PIN_3; //选择第3脚,PA3是USART2_RX
GPIO_InitStructureure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
GPIO_InitStructureure.Pull = GPIO_PULLUP; //引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
GPIO_InitStructureure.Alternate = GPIO_AF4_USART2; //将引脚复用为USART2
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//将PA1初始化为USART2_RX
//串口引脚映射结束/
/HAL_UART_MspInit结束
UART_HandleStructureure.Instance = USART2; //接口为USART2
UART_HandleStructureure.Init.BaudRate = baudrate; //波特率为115200bps
UART_HandleStructureure.Init.WordLength = UART_WORDLENGTH_8B; //串口字长度为8
UART_HandleStructureure.Init.StopBits = UART_STOPBITS_1; //串口停止位为1位
UART_HandleStructureure.Init.Parity = UART_PARITY_NONE; //串口无需奇偶校验
UART_HandleStructureure.Init.HwFlowCtl = UART_HWCONTROL_NONE; //串口无硬件流程控制
UART_HandleStructureure.Init.Mode = UART_MODE_TX_RX; //串口工作模式为发送和接收模式
UART_HandleStructureure.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_NO_INIT;//不使用自动波特率
// UART_HandleStructureure.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_AUTOBAUDRATE_INIT;//使用自动波特率配置
// UART_HandleStructureure.AdvancedInit.AutoBaudRateEnable=UART_ADVFEATURE_AUTOBAUDRATE_ENABLE;//自动波特率使能
// UART_HandleStructureure.AdvancedInit.AutoBaudRateMode=UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT;//自动波特率模式
HAL_UART_Init(&UART_HandleStructureure);
//根据UART_HandleStructureure型结构初始化USART2
__HAL_RCC_SYSCFG_CLK_ENABLE();//使能RCC外设时钟
HAL_SYSCFG_DMA_Req(0x0807);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA1_MAP[4:0]=7,将DMA通道1映射到USART2_TX
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
Usart2Send_DMA1_CH1_config();
Usart2Receive_DMA1_CH2_config();
// HAL_NVIC_SetPriority(USART2_IRQn, 0x01, 0);
//设置串口2中断优先级为0x01,0无意义.USART2_IRQn表示中断源为串口2
// __HAL_UART_ENABLE_IT(&UART_HandleStructureure, UART_IT_RXNE);
//开启串口接收中断
//串口接收数据时,使能"接收数据寄存器不为空"则产生中断(位RXNE=1)
//Enable the UART Data Register not empty Interrupt
__HAL_UART_DISABLE_IT(&UART_HandleStructureure, UART_IT_RXNE);
//关闭串口接收中断
//串口接收数据时,不使能"接收数据寄存器不为空"则产生中断(位RXNE=0)
__HAL_UART_DISABLE_IT(&UART_HandleStructureure, UART_IT_TXE);
//串口发送数据时,不使能"串口发送数据寄存器为空"产生中断(位TXE=0)
//Disable the UART Transmit Complete Interrupt
__HAL_UART_DISABLE_IT(&UART_HandleStructureure,UART_IT_TC);
//串口发送数据时,不使能"串口发送完成"产生中断(位TC=1)
/在串口中断服务函数中发送数据配置结束//
// HAL_NVIC_EnableIRQ(USART2_IRQn);
//使能串口2中断
//USART2_IRQn表示中断源为串口2
}
#endif
#if USART2Type ==1
//函数功能:
//PA0是为USART2_TX,PA1是USART2_RX
//中断优先级为0x01
//波特率为115200,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,只允许接收中断,并使能串口
void USART2_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructureure;
UART_HandleTypeDef UART_HandleStructureure;
/HAL_UART_MspInit开始
__HAL_RCC_USART2_CLK_ENABLE();//使能USART2外设时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
//串口引脚映射开始/
GPIO_InitStructureure.Pin = GPIO_PIN_0; //选择第0脚,PA0是为USART2_TX
GPIO_InitStructureure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
GPIO_InitStructureure.Pull = GPIO_PULLUP; //引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
GPIO_InitStructureure.Alternate = GPIO_AF9_USART2; //将引脚复用为USART2
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//将PA0初始化为USART2_TX
GPIO_InitStructureure.Pin = GPIO_PIN_1; //选择第1脚,PA1是USART2_RX
GPIO_InitStructureure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
GPIO_InitStructureure.Pull = GPIO_PULLUP; //引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
GPIO_InitStructureure.Alternate = GPIO_AF9_USART2; //将引脚复用为USART2
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//将PA1初始化为USART2_RX
//串口引脚映射结束/
/HAL_UART_MspInit结束
UART_HandleStructureure.Instance = USART2; //接口为USART2
UART_HandleStructureure.Init.BaudRate = baudrate; //波特率为115200bps
UART_HandleStructureure.Init.WordLength = UART_WORDLENGTH_8B; //串口字长度为8
UART_HandleStructureure.Init.StopBits = UART_STOPBITS_1; //串口停止位为1位
UART_HandleStructureure.Init.Parity = UART_PARITY_NONE; //串口无需奇偶校验
UART_HandleStructureure.Init.HwFlowCtl = UART_HWCONTROL_NONE; //串口无硬件流程控制
UART_HandleStructureure.Init.Mode = UART_MODE_TX_RX; //串口工作模式为发送和接收模式
UART_HandleStructureure.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_NO_INIT;//不使用自动波特率
// UART_HandleStructureure.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_AUTOBAUDRATE_INIT;//使用自动波特率配置
// UART_HandleStructureure.AdvancedInit.AutoBaudRateEnable=UART_ADVFEATURE_AUTOBAUDRATE_ENABLE;//自动波特率使能
// UART_HandleStructureure.AdvancedInit.AutoBaudRateMode=UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT;//自动波特率模式
HAL_UART_Init(&UART_HandleStructureure);
//根据UART_HandleStructureure型结构初始化USART2
__HAL_RCC_SYSCFG_CLK_ENABLE();//使能RCC外设时钟
HAL_SYSCFG_DMA_Req(0x0807);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA1_MAP[4:0]=7,将DMA通道1映射到USART2_TX
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
Usart2Send_DMA1_CH1_config();
Usart2Receive_DMA1_CH2_config();
// HAL_NVIC_SetPriority(USART2_IRQn, 0x01, 0);
//设置串口2中断优先级为0x01,0无意义.USART2_IRQn表示中断源为串口2
// __HAL_UART_ENABLE_IT(&UART_HandleStructureure, UART_IT_RXNE);
开启串口接收中断
//串口接收数据时,使能"接收数据寄存器不为空"则产生中断(位RXNE=1)
//Enable the UART Data Register not empty Interrupt
__HAL_UART_DISABLE_IT(&UART_HandleStructureure, UART_IT_RXNE);
//关闭串口接收中断
//串口接收数据时,不使能"接收数据寄存器不为空"则产生中断(位RXNE=0)
__HAL_UART_DISABLE_IT(&UART_HandleStructureure, UART_IT_TXE);
//串口发送数据时,不使能"串口发送数据寄存器为空"产生中断(位TXE=0)
//Disable the UART Transmit Complete Interrupt
__HAL_UART_DISABLE_IT(&UART_HandleStructureure,UART_IT_TC);
//串口发送数据时,不使能"串口发送完成"产生中断(位TC=1)
/在串口中断服务函数中发送数据配置结束//
// HAL_NVIC_EnableIRQ(USART2_IRQn);
//使能串口2中断
//USART2_IRQn表示中断源为串口2
}
#endif
//重定义fputc函数
//函数功能:发送ch的值给USART2串口
int fputc(int ch, FILE *f)
{
_HAL_UART_SendByte(USART2, (unsigned char) ch);
while( _HAL_UART_GET_FLAG(USART2,USART_SR_TC)!= SET); //等待发送完成标志位被置1
return ch;
}
//函数功能:串口2发送一个字节
void USART2_SendByte( unsigned char ch )
{
_HAL_UART_SendByte(USART2, (unsigned char) ch);
while( _HAL_UART_GET_FLAG(USART2,USART_SR_TC)!= SET); //等待发送完成标志位被置1
}
void Usart2Send_DMA1_CH1_config(void)
{
DMA_HandleTypeDef hdma_usart2_tx;
__HAL_RCC_SYSCFG_CLK_ENABLE();//使能RCC外设时钟
__HAL_RCC_DMA_CLK_ENABLE(); //使能DMA时钟
///配置串口DMA发送开始
hdma_usart2_tx.DmaBaseAddress = DMA1; //选择DMA1传输
hdma_usart2_tx.Instance = DMA1_Channel1; //DMA1通道1
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;//DMA传输方向:从内存到外设
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; //不使能外设地址增量
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; //使能内存地址增量
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据宽度为8位
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存储器数据宽度为8位
hdma_usart2_tx.Init.Mode = DMA_NORMAL; //DMA普通模式
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW; //DMA通道1优先级为低
HAL_DMA_DeInit(&hdma_usart2_tx);
HAL_DMA_Init(&hdma_usart2_tx);
///配置串口DMA发送结束
// HAL_DMA_ChannelMap(&hdma_usart2_tx,DMA_CHANNEL_MAP_USART2_TX);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA1_MAP[4:0]=7,将DMA通道1映射到USART2_TX
}
void Usart2Receive_DMA1_CH2_config(void)
{
DMA_HandleTypeDef hdma_usart2_rx;
__HAL_RCC_SYSCFG_CLK_ENABLE();//使能RCC外设时钟
__HAL_RCC_DMA_CLK_ENABLE(); //使能DMA时钟
hdma_usart2_rx.DmaBaseAddress = DMA1; //选择DMA1传输
hdma_usart2_rx.Instance = DMA1_Channel2; //DMA1通道2
// HAL_DMA_ChannelMap(&hdma_usart2_rx,DMA_CHANNEL_MAP_USART2_RX);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
///配置串口DMA接收开始
hdma_usart2_rx.DmaBaseAddress = DMA1; //选择DMA1传输
hdma_usart2_rx.Instance = DMA1_Channel2; //DMA1通道2
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;//DMA传输方向:从外设到内存
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; //不使能外设地址增量
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; //使能内存地址增量
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据宽度为8位
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存储器数据宽度为8位
hdma_usart2_rx.Init.Mode = DMA_NORMAL; //DMA普通模式
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW; //DMA通道2优先级为低
HAL_DMA_DeInit(&hdma_usart2_rx);
HAL_DMA_Init(&hdma_usart2_rx);
///配置串口DMA接收结束
// HAL_DMA_ChannelMap(&hdma_usart2_rx,DMA_CHANNEL_MAP_USART2_RX);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
__HAL_DMA_DISABLE(&hdma_usart2_rx);
//将"DMA通道x配置寄存器DMA_CCRx"中的EN=0,DMA通道x不使能
// My_DMA_SetConfig(&hdma_usart2_rx,(uint32_t)&USART2->DR,(uint32_t)&USART2_RX_Buffer,USART2_RX_Buffer_Size);
My_DMA1_SetConfig(DMA1_Channel2,(uint32_t)&USART2->DR,(uint32_t)&USART2_RX_Buffer,USART2_RX_Buffer_Size,0);
__HAL_DMA_ENABLE(&hdma_usart2_rx);
//将"DMA通道x配置寄存器DMA_CCRx"中的EN=1,DMA通道x使能
_HAL_UART_CLEAR_FLAG(USART2,UART_FLAG_RXNE);
SET_BIT(USART2->CR3, USART_CR3_DMAR);
//将"串口控制寄存器3(USART_CR3)中的DMAR位"置1,接收时使能DMA
USART2_RX_Buffer_StartIndex=USART2_RX_Buffer_Size-_HAL_DMA_Transfer_Number_Get(DMA1_Channel2);//读DMA1通道2剩余空间
USART2_RX_Buffer_EndIndex=USART2_RX_Buffer_StartIndex;
}
//函数功能:将USART2_TX_Buffer[]中的字符串通过"DMA通道1"写入串口发送寄存器
void Usart2Send1(void)
{
DMA_HandleTypeDef hdma_usart2_tx;
///配置串口DMA发送开始
hdma_usart2_tx.DmaBaseAddress = DMA1; //选择DMA1传输
hdma_usart2_tx.Instance = DMA1_Channel1; //DMA1通道1
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;//DMA传输方向:从内存到外设
__HAL_DMA_DISABLE(&hdma_usart2_tx);
My_DMA_SetConfig(&hdma_usart2_tx, (uint32_t)&USART2_TX_Buffer,(uint32_t)&USART2->DR,strlen( (char*)USART2_TX_Buffer ));
__HAL_DMA_ENABLE(&hdma_usart2_tx);
//将"DMA通道x配置寄存器DMA_CCRx"中的EN=1,通道x使能
_HAL_UART_CLEAR_FLAG(USART2,UART_FLAG_TC);
SET_BIT(USART2->CR3, USART_CR3_DMAT);
//将"串口控制寄存器3(USART_CR3)中的DMAT位"置1,发送时使能DMA
while( __HAL_DMA_GET_FLAG(&hdma_usart2_tx,DMA_FLAG_TC1) == RESET)
{//等待"DMA通道1"传输完成
}
__HAL_DMA_CLEAR_FLAG(DMA1_Channel1,DMA_FLAG_TC1);
//将"DMA中断标志位清除寄存器DMA_IFCR"中的CTCIF1=0,通道1传输完成标志清零
}
//函数功能:将USART2_TX_Buffer[]中的字符串通过"DMA通道1"写入串口发送寄存器
void Usart2Send(void)
{
_HAL_DMA_DISABLE(DMA1_Channel1);
//将"DMA通道1配置寄存器DMA_CCR1"中EN=0,DMA通道1不使能
My_DMA1_SetConfig(DMA1_Channel1,(uint32_t)&USART2_TX_Buffer,(uint32_t)&USART2->DR,strlen( (char*)USART2_TX_Buffer),1);
_HAL_DMA_ENABLE(DMA1_Channel1);
//将"DMA通道1配置寄存器DMA_CCR1"中的EN=1,通道1使能
_HAL_UART_CLEAR_FLAG(USART2,UART_FLAG_TC);
SET_BIT(USART2->CR3, USART_CR3_DMAT);
//将"串口控制寄存器3(USART_CR3)中的DMAT位"置1,发送时使能DMA
while( __HAL_DMA_GET_FLAG(DMA1,DMA_FLAG_TC1) == RESET)
{//等待"DMA通道1"传输完成
}
__HAL_DMA_CLEAR_FLAG(DMA1_Channel1,DMA_FLAG_TC1);
//将"DMA中断标志位清除寄存器DMA_IFCR"中的CTCIF1=0,通道1传输完成标志清零
}
//函数功能:
//将"串口接收寄存器"通过"DMA1通道2"写入USART2_RX_Buffer[]
//接收完成后,将接收到的有效数据拷贝到USART2_RX1_Buffer[]中
void Usart2Receive(void)
{
uint8_t i;
uint8_t len_New;
uint8_t len_Old;
len_New=_HAL_DMA_Transfer_Number_Get(DMA1_Channel2);//读"DMA1通道2"剩余空间
len_Old=len_New;
USART2_RX_Buffer_EndIndex=USART2_RX_Buffer_Size-len_Old;
if(USART2_RX_Buffer_StartIndex!=USART2_RX_Buffer_EndIndex)//发现新数据
{
for(i=0;i<10;i++)//查询是否接收完成
{
delay_us(100);
len_New=_HAL_DMA_Transfer_Number_Get(DMA1_Channel2);//读DMA1通道2剩余空间
if(len_New!=len_Old)//接收没有完成
{
len_Old=len_New;
i=0;
}
else//接收超时,表示DMA接收完成
{
USART2_RX_Buffer_EndIndex=USART2_RX_Buffer_Size-len_Old;
//记录尾部
for(i=0;USART2_RX_Buffer_StartIndex!=USART2_RX_Buffer_EndIndex;)
{//将接收到的数据保存到USART2_RX1_Buffer[]中;
USART2_RX1_Buffer[i]=USART2_RX_Buffer[USART2_RX_Buffer_StartIndex];
i++;
USART2_RX_Buffer_StartIndex++;
}
USART2_RX1_Buffer[i]='\0';
i=11;//接收完成
}
}
strcpy((char*)USART2_TX_Buffer,(char*)USART2_RX1_Buffer);
Usart2Send();//将接收到的数据回传
Usart2Receive_DMA1_CH2_config();//重新初始化串口到DMA接收
}
}
#ifndef __USART2_H
#define __USART2_H
#include "py32f0xx_hal.h"
#define USART2Type 1 //
#define _HAL_UART_SendByte(__INSTANCE__, __DATA__) (__INSTANCE__)->DR = ( (__DATA__) & (uint16_t)0x01FF );
//将(__DATA__)写入串口发送缓冲区
#define _HAL_UART_ReceiveByte(__INSTANCE__) ( (uint16_t)( (__INSTANCE__)->DR & (uint16_t)0x01FF) )
//读串口接收缓冲区
#define _HAL_UART_GET_FLAG(__INSTANCE__, __FLAG__) ( ( (__INSTANCE__)->SR & (__FLAG__) ) == (__FLAG__) )
//读串口中断标志位
//(__FLAG__)=UART_IT_RXNE,读"串口接收寄存器为非空时产生的中断标志位"
//(__FLAG__)=UART_IT_PE,读"串口奇偶校验错误产生的中断标志位"
//(__FLAG__)=UART_IT_ERR,读"帧错误、噪音错误和溢出错误时产生的中断标志位"
//(__FLAG__)=UART_IT_TXE时,读"串口发送寄存器为空产生的中断标志位"
//(__FLAG__)=UART_IT_TC时,读"发送完成产生的中断标志位"
#define _HAL_UART_CLEAR_FLAG(__INSTANCE__, __FLAG__) ( (__INSTANCE__)->SR = ~(__FLAG__) )
//清除串口中断标志位
//(__FLAG__)=UART_IT_RXNE,清除"串口接收寄存器为非空时产生的中断标志位"
//(__FLAG__)=UART_IT_PE,清除"串口奇偶校验错误产生的中断标志位"
//(__FLAG__)=UART_IT_ERR,清除"帧错误、噪音错误和溢出错误时产生的中断标志位"
//(__FLAG__)=UART_IT_TXE时,清除"串口发送寄存器为空产生的中断标志位"
//(__FLAG__)=UART_IT_TC时,清除"发送完成产生的中断标志位"
#define _HAL_UART_ENABLE_IT(__INSTANCE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == UART_CR1_REG_INDEX)? ((__INSTANCE__)->CR1 |= ((__INTERRUPT__) & UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__INSTANCE__)->CR2 |= ((__INTERRUPT__) & UART_IT_MASK)): \
((__INSTANCE__)->CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
//设置串口中断使能位
//(__INTERRUPT__)=UART_IT_RXNE,设置"串口接收寄存器为非空"时,使其产生中断
//(__INTERRUPT__)=UART_IT_PE,设置"串口奇偶校验错误"时,使其产生中断
//(__INTERRUPT__)=UART_IT_ERR,设置"帧错误、噪音错误和溢出错误"时,使其产生中断
//(__INTERRUPT__)=UART_IT_TXE时,设置"串口发送寄存器为空"时,使其产生中断
//(__INTERRUPT__)=UART_IT_TC时,设置"串口发送寄存器发送完成"时,使其产生中断
#define _HAL_UART_DISABLE_IT(__INSTANCE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == UART_CR1_REG_INDEX)? ((__INSTANCE__)->CR1 &= ~((__INTERRUPT__) & UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__INSTANCE__)->CR2 &= ~((__INTERRUPT__) & UART_IT_MASK)): \
((__INSTANCE__)->CR3 &= ~ ((__INTERRUPT__) & UART_IT_MASK)))
//设置串口中断不使能
//(__INTERRUPT__)=UART_IT_RXNE,设置"串口接收寄存器为非空"时,不使其产生中断
//(__INTERRUPT__)=UART_IT_PE,设置"串口奇偶校验错误"时,不使其产生中断
//(__INTERRUPT__)=UART_IT_ERR,设置"帧错误、噪音错误和溢出错误"时,不使其产生中断
//(__INTERRUPT__)=UART_IT_TXE时,设置"串口发送寄存器为空"时,不使其产生中断
//(__INTERRUPT__)=UART_IT_TC时,设置"串口发送寄存器发送完成"时,不使其产生中断
#define USART2_TX_Buffer_Size 15
extern uint8_t USART2_TX_Buffer[USART2_TX_Buffer_Size];
extern uint8_t USART2_TX_Buffer_Send_Index;//USART2_TX_Buffer[]的发送索引值;
extern uint8_t USART2_TX_Buffer_Load_Index;//USART2_TX_Buffer[]的装载索引值
extern uint8_t USART2_TX_Completed_Flag;
extern uint8_t USART2_TX_Overtime_Conter;//USART2发送超时计数器
#define USART2_RX_Buffer_Size 125
extern uint8_t USART2_RX_Buffer[USART2_RX_Buffer_Size]; //USART2接收缓冲区数组
extern uint8_t USART1_RX_Buffer_StartIndex;//USART1_RX_Buffer[]的装载索引值
extern uint8_t USART1_RX_Buffer_EndIndex;//USART1_RX_Buffer[]的装载索引值
#define USART2_RX1_Buffer_Size 125
extern uint8_t USART2_RX1_Buffer[USART2_RX1_Buffer_Size]; //USART2接收缓冲区数组
extern void USART2_Init(uint32_t baudrate);
extern void Usart2Send(void);
extern void Usart2Receive(void);
#endif /* __USART2_H */
#include "My_DMA.h"
void My_DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);
void My_DMA1_SetConfig(DMA_Channel_TypeDef *instance, uint32_t srcAddress, uint32_t dstAddress, uint32_t dataLength,uint8_t direction);
#define _HAL_DMA_CLEAR_FLAG(__INSTANCE__, __FLAG__) ( (__INSTANCE__)->IFCR |= (__FLAG__) )
//清除串口中断标志位
//(__FLAG__)=0x01,CGIF1=1,清除"通道1全局中断标志"
//(__FLAG__)=0x10,CGIF2=1,清除"通道2全局中断标志"
//(__FLAG__)=0x100,CGIF3=1,清除"通道3全局中断标志"
//SrcAddress:源数据地址
//DstAddress:目的数据地址
//DataLength:传输数据长度
void My_DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
/* Clear all flags */
if(hdma->Instance==DMA1_Channel1) hdma->ChannelIndex=0x01;
if(hdma->Instance==DMA1_Channel2) hdma->ChannelIndex=0x04;
if(hdma->Instance==DMA1_Channel3) hdma->ChannelIndex=0x08;
hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << hdma->ChannelIndex);
//将"DMA中断标志位清除寄存器DMA_IFCR"中的CGIFx设置为1,将DMA通道x全局中断标志清零
/* Configure DMA Channel data length */
hdma->Instance->CNDTR = DataLength;//传输数据长度
/* Memory to Peripheral */
if((hdma->Init.Direction) == DMA_MEMORY_TO_PERIPH)//从内存向外设传输
{
/* Configure DMA Channel destination address */
hdma->Instance->CPAR = DstAddress;//目的数据地址
/* Configure DMA Channel source address */
hdma->Instance->CMAR = SrcAddress;//源数据地址
}
/* Peripheral to Memory */
else//从外设向内存传输
{
/* Configure DMA Channel source address */
hdma->Instance->CPAR = SrcAddress;//源数据地址
/* Configure DMA Channel destination address */
hdma->Instance->CMAR = DstAddress;//目的数据地址
}
}
//srcAddress:源数据地址
//dstAddress:目的数据地址
//dataLength:传输数据长度
//direction>0,从内存向外设传输;direction=0,从外设向内存传输
void My_DMA1_SetConfig(DMA_Channel_TypeDef *instance, uint32_t srcAddress, uint32_t dstAddress, uint32_t dataLength,uint8_t direction)
{
uint8_t ChannelIndex;
/* Clear all flags */
if(instance==DMA1_Channel1) ChannelIndex=0x01;
if(instance==DMA1_Channel2) ChannelIndex=0x04;
if(instance==DMA1_Channel3) ChannelIndex=0x08;
DMA1->IFCR = (DMA_ISR_GIF1 << ChannelIndex);
//将"DMA中断标志位清除寄存器DMA_IFCR"中的CGIFx设置为1,将DMA通道x全局中断标志清零
/* Configure DMA Channel data length */
instance->CNDTR = dataLength;//传输数据长度
/* Memory to Peripheral */
if( direction )//从内存向外设传输
{
/* Configure DMA Channel destination address */
instance->CPAR = dstAddress;//目的数据地址
/* Configure DMA Channel source address */
instance->CMAR = srcAddress;//源数据地址
}
/* Peripheral to Memory */
else//从外设向内存传输
{
/* Configure DMA Channel source address */
instance->CPAR = srcAddress;//源数据地址
/* Configure DMA Channel destination address */
instance->CMAR = dstAddress;//目的数据地址
}
}
#ifndef __My_DMA_H
#define __My_DMA_H
#include "py32f0xx_hal.h"
#define _HAL_DMA_ENABLE(__INSTANCE__) ((__INSTANCE__)->CCR |= DMA_CCR_EN)
//将"DMA通道x配置寄存器DMA_CCRx"中EN=1,DMA通道x使能
#define _HAL_DMA_DISABLE(__INSTANCE__) ((__INSTANCE__)->CCR &= ~DMA_CCR_EN)
//将"DMA通道x配置寄存器DMA_CCRx"中EN=0,DMA通道x不使能
#define _HAL_DMA_Transfer_Number_Get(__INSTANCE__) ( (__INSTANCE__)->CNDTR )
//读DMA1通道x剩余空间
extern void My_DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);
extern void My_DMA1_SetConfig(DMA_Channel_TypeDef *instance, uint32_t srcAddress, uint32_t dstAddress, uint32_t dataLength,uint8_t direction);
#endif /* __My_DMA_H */
#include "py32f0xx_hal.h"
#include "SystemClock.h"
#include "delay.h"
#include "LED.h"
#include "USART2.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "SystemClock.h"
const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
int main(void)
{
HSE_Config();
// HAL_Init();//systick初始化
delay_init();
HAL_Delay(1000);
USART2_Init(115200);
//PA0是为USART2_TX,PA1是USART2_RX
//中断优先级为0x01
//波特率为115200,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,只允许接收中断,并使能串口
// printf("%s",CPU_Reset_REG);
strcpy((char*)USART2_TX_Buffer,CPU_Reset_REG);
Usart2Send();
TIM1_LED_Init();
TIM3_LED_Init();
TIM_LED_Init();
while (1)
{
delay_ms(100);
Usart2Receive();
// strcpy((char*)USART2_TX_Buffer,"A234567890\r\n");
// Usart2Send();
}
}
三、测试结果
四、不用HAL库DMA函数的目的
因为HAL库的DMA传输函数,需要将UART_HandleTypeDef,DMA_HandleTypeDef结构型数据申请为全局变量,才可以使用,太浪费内存了。
UART_HandleTypeDef UART_HandleStructureure;
DMA_HandleTypeDef hdma_usart2_tx;
DMA_HandleTypeDef hdma_usart2_rx;
实在太浪费了。
贴出官方的测试代码,以示如下:
#include "USART2.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
//PA0 ------> USART2_TX
//PA1 ------> USART2_RX
UART_HandleTypeDef UART_HandleStructureure;
DMA_HandleTypeDef hdma_usart2_tx;
DMA_HandleTypeDef hdma_usart2_rx;
uint8_t aTxBuffer[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t aRxBuffer[12] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
void USART2_Init(uint32_t baudrate);
void USART2_Work(void);
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_InitStructureure;
if(huart->Instance==USART2)
{
__HAL_RCC_USART2_CLK_ENABLE();//使能USART2外设时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_SYSCFG_CLK_ENABLE();//使能RCC外设时钟
__HAL_RCC_DMA_CLK_ENABLE(); //使能DMA时钟
//串口引脚映射开始/
GPIO_InitStructureure.Pin = GPIO_PIN_0; //选择第0脚,PA0是为USART2_TX
GPIO_InitStructureure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
GPIO_InitStructureure.Pull = GPIO_PULLUP; //引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
GPIO_InitStructureure.Alternate = GPIO_AF9_USART2; //将引脚复用为USART2
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//将PA0初始化为USART2_TX
GPIO_InitStructureure.Pin = GPIO_PIN_1; //选择第1脚,PA1是USART2_RX
GPIO_InitStructureure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
GPIO_InitStructureure.Pull = GPIO_PULLUP; //引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
GPIO_InitStructureure.Alternate = GPIO_AF9_USART2; //将引脚复用为USART2
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//将PA1初始化为USART2_RX
//串口引脚映射结束/
HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
//设置串口2中断优先级为0x00,1无意义
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 1);
//设置DMA1通道1中断优先级为0x00,1无意义
HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 1, 0);
//设置DMA1通道2和通道3中断优先级为0x01,0无意义
}
}
//函数功能:
//PA0是为USART2_TX,PA1是USART2_RX
//中断优先级为0x01
//波特率为115200,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,只允许接收中断,并使能串口
void USART2_Init(uint32_t baudrate)
{
UART_HandleStructureure.Instance = USART2; //接口为USART2
UART_HandleStructureure.Init.BaudRate = baudrate; //波特率为115200bps
UART_HandleStructureure.Init.WordLength = UART_WORDLENGTH_8B; //串口字长度为8
UART_HandleStructureure.Init.StopBits = UART_STOPBITS_1; //串口停止位为1位
UART_HandleStructureure.Init.Parity = UART_PARITY_NONE; //串口无需奇偶校验
UART_HandleStructureure.Init.HwFlowCtl = UART_HWCONTROL_NONE; //串口无硬件流程控制
UART_HandleStructureure.Init.Mode = UART_MODE_TX_RX; //串口工作模式为发送和接收模式
UART_HandleStructureure.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_NO_INIT;//不使用自动波特率
// UART_HandleStructureure.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_AUTOBAUDRATE_INIT;//使用自动波特率配置
// UART_HandleStructureure.AdvancedInit.AutoBaudRateEnable=UART_ADVFEATURE_AUTOBAUDRATE_ENABLE;//自动波特率使能
// UART_HandleStructureure.AdvancedInit.AutoBaudRateMode=UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT;//自动波特率模式
HAL_UART_Init(&UART_HandleStructureure);
//根据UART_HandleStructureure型结构初始化USART2
// HAL_SYSCFG_DMA_Req(0x0807);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA1_MAP[4:0]=7,将DMA通道1映射到USART2_TX
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
hdma_usart2_tx.DmaBaseAddress = DMA1; //选择DMA1传输
hdma_usart2_tx.Instance = DMA1_Channel1; //DMA1通道1
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;//DMA传输方向:从内存到外设
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; //不使能外设地址增量
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; //使能内存地址增量
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据宽度为8位
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存储器数据宽度为8位
hdma_usart2_tx.Init.Mode = DMA_NORMAL; //DMA普通模式
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW; //DMA通道1优先级为低
HAL_DMA_Init(&hdma_usart2_tx);
__HAL_LINKDMA(&UART_HandleStructureure, hdmatx, hdma_usart2_tx);
//这里必须要连接DMA句柄
//令UART_HandleStructureure.hdmatx=&hdma_usart2_tx;//记录
//令hdma_usart2_tx.Parent=&UART_HandleStructureure;//记录
hdma_usart2_rx.DmaBaseAddress = DMA1; //选择DMA1传输
hdma_usart2_rx.Instance = DMA1_Channel2; //DMA1通道2
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;//DMA传输方向:从外设到内存
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; //不使能外设地址增量
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; //使能内存地址增量
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据宽度为8位
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存储器数据宽度为8位
hdma_usart2_rx.Init.Mode = DMA_NORMAL; //DMA普通模式
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW; //DMA通道2优先级为低
HAL_DMA_Init(&hdma_usart2_rx);
__HAL_LINKDMA(&UART_HandleStructureure, hdmarx, hdma_usart2_rx);
//连接DMA句柄
//令UART_HandleStructureure.hdmarx=&hdma_usart2_rx;//记录
//令hdma_usart2_rx.Parent=&UART_HandleStructureure;//记录
HAL_DMA_ChannelMap(&hdma_usart2_tx,DMA_CHANNEL_MAP_USART2_TX);
HAL_DMA_ChannelMap(&hdma_usart2_rx,DMA_CHANNEL_MAP_USART2_RX);
// HAL_SYSCFG_DMA_Req(0x0807);
//配置"SYSCFG配置寄存器3(SYSCFG_CFGR3)"
//DMA1_MAP[4:0]=7,将DMA通道1映射到USART2_TX
//DMA2_MAP[4:0]=8,将DMA通道2映射到USART2_RX
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
HAL_NVIC_EnableIRQ(USART2_IRQn);
//使能串口2中断
//USART2_IRQn表示中断源为串口2
}
//重定义fputc函数
//函数功能:发送ch的值给USART2串口
int fputc(int ch, FILE *f)
{
_HAL_UART_SendByte(USART2, (unsigned char) ch);
while( _HAL_UART_GET_FLAG(USART2,USART_SR_TC)!= SET); //等待发送完成标志位被置1
return ch;
}
//函数功能:串口2发送一个字节
void USART2_SendByte( unsigned char ch )
{
_HAL_UART_SendByte(USART2, (unsigned char) ch);
while( _HAL_UART_GET_FLAG(USART2,USART_SR_TC)!= SET); //等待发送完成标志位被置1
}
void USART2_Work(void)
{
/* 通过DMA方式接收数据 */
if (HAL_UART_Receive_DMA(&UART_HandleStructureure, (uint8_t *)aRxBuffer, 12) != HAL_OK)
{
}
/* 等待接收数据完成 */
while(HAL_UART_GetState(&UART_HandleStructureure) != HAL_UART_STATE_READY)
{}
/* 通过DMA方式发送数据 */
if (HAL_UART_Transmit_DMA(&UART_HandleStructureure, (uint8_t *)aRxBuffer, 12) != HAL_OK)
{
}
/* 等待发送数据完成 */
while(HAL_UART_GetState(&UART_HandleStructureure) != HAL_UART_STATE_READY)
{}
}
//DMA通道1负责串口发送
void DMA1_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_tx);
//若为"DMA通道x半传输中断",则清除“DMA通道x半传输中断”标志位
//若为“DMA通道x传输完成中断”,则清除“DMA通道x传输完成中断”标志位
//若为"DMA通道x传输错误中断",则不使能"DMA通道x传输完成中断"、"DMA通道x半传输中断"和"通道x传输错误中断",清除通道x全局中断标志
// HAL_DMA_IRQHandler(UART_HandleStructureure.hdmatx);
}
//DMA通道2负责串口接收
void DMA1_Channel2_3_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_rx);
//若为"DMA通道x半传输中断",则清除“DMA通道x半传输中断”标志位
//若为“DMA通道x传输完成中断”,则清除“DMA通道x传输完成中断”标志位
//若为"DMA通道x传输错误中断",则不使能"DMA通道x传输完成中断"、"DMA通道x半传输中断"和"通道x传输错误中断",清除通道x全局中断标志
// HAL_DMA_IRQHandler(UART_HandleStructureure.hdmarx);
}
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART_HandleStructureure);
}
耗内存,占用FLASH较小。FLASH那么大,节省没啥用。思绪再三,还是修改掉。