本帖最后由 huaimengzi 于 2025-6-10 23:12 编辑
单片机为GD32E230C8T6, 利用IDLE空闲中断+DMA接收,利用DMA发送及DMA发送完成后产生中断,在中断中将RS485切换方向。原本是单片机收到数据后,将数据原样发回给串口助手,但程序一上电,就进入到了DMA中断去了,求各位大神帮忙指导一下原因。
/************************************************************************************************
**文 件 名:Uart0_GPIO_Config
**说 明:串口的GPIO配置
************************************************************************************************/
static void Uart0_GPIO_Config(void)
{
/*1.开启Usart0对应的GPIO的时钟*/
rcu_periph_clock_enable(Uart0_Info.RcuGpio);
/*2.连接端口到USART的引脚*/
gpio_af_set(Uart0_Info.gpio, BSP_USART0_AF, Uart0_Info.TxPin);
gpio_af_set(Uart0_Info.gpio, BSP_USART0_AF, Uart0_Info.RxPin);
/*3.配置TX,RX两个引脚为复用模式,配置为上拉模式*/
gpio_mode_set(Uart0_Info.gpio, GPIO_MODE_AF, GPIO_PUPD_PULLUP, Uart0_Info.TxPin);
gpio_mode_set(Uart0_Info.gpio, GPIO_MODE_AF, GPIO_PUPD_PULLUP, Uart0_Info.RxPin);
/*4.配置TX,RX为推挽输出,速度最大为10MHZ*/
gpio_output_options_set(Uart0_Info.gpio, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, Uart0_Info.TxPin);
gpio_output_options_set(Uart0_Info.gpio, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, Uart0_Info.RxPin);
/*5.配置RS485的Enable引脚*/
rcu_periph_clock_enable(BSP_RS485EN0_RCU); //开启时钟
gpio_mode_set(BSP_RS485EN0_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, BSP_RS485EN0_PIN); //输出,浮空
gpio_output_options_set(BSP_RS485EN0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, BSP_RS485EN0_PIN);//配置为推挽输出
}
/************************************************************************************************
**文 件 名:Uart0_Comm_Config
**说 明:串口配置
************************************************************************************************/
static void Uart0_Comm_Config(uint32_t BaudRate)
{
/*1.开启USART的时钟*/
rcu_periph_clock_enable(Uart0_Info.RcuUart);
/*2.配置串口参数*/
usart_deinit(Uart0_Info.UartNumber); //复位串口
usart_baudrate_set(Uart0_Info.UartNumber, BaudRate); //设置波特率
usart_word_length_set(Uart0_Info.UartNumber, USART_WL_8BIT); //配置数据位的长度8
usart_stop_bit_set(Uart0_Info.UartNumber, USART_STB_1BIT); //停止位1位
usart_parity_config(Uart0_Info.UartNumber, USART_PM_NONE); //设置无校验
usart_transmit_config(Uart0_Info.UartNumber, USART_TRANSMIT_ENABLE); //使能发送
usart_receive_config(Uart0_Info.UartNumber, USART_RECEIVE_ENABLE); //使能接收
/*3.使能串口*/
usart_enable(Uart0_Info.UartNumber);
/*4.配置为UART0接收空闲中断*/
//4.1 开启内核串口中断
nvic_irq_enable(Uart0_Info.irq, 0);
//4.2 开启外设接收空闲中断
usart_interrupt_enable(Uart0_Info.UartNumber, USART_INT_IDLE);
}
/*********************************************************************************************************
* 函数名称:static void DMA_USART0_Init(void)
* 函数功能:采用空闲中断+DMA接收,采用DMA发送
*********************************************************************************************************/
static void DMA_USART0_Init(void)
{
/*0.定义DMA结构体*/
dma_parameter_struct dmaStruct;
/*1.使能DMA时钟;*/
rcu_periph_clock_enable(Uart0_Info.Rcu_DMA);
/******************************以下DMA_USART0_TX配置******************************/
/*2.复位DMA的TX发送通道和复位结构体;*/
dma_deinit(Uart0_Info.DMA_CH_TX);
dma_struct_para_init(&dmaStruct);
/*3. 为结构体赋值*/
dmaStruct.direction = DMA_MEMORY_TO_PERIPHERAL;
dmaStruct.memory_addr = (uint32_t)Uart0_Comm.SendBuff;
dmaStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dmaStruct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dmaStruct.number = TXD0_BUFF_SIZE;
dmaStruct.periph_addr = USART0_TDATA_ADDRESS;
dmaStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dmaStruct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dmaStruct.priority = DMA_PRIORITY_HIGH;
/*4. 把结构体变量的值注入DMA的TX通道参数中*/
dma_init(Uart0_Info.DMA_CH_TX, &dmaStruct);
/*5. configure DMA mode */
dma_circulation_disable(Uart0_Info.DMA_CH_TX);
dma_memory_to_memory_disable(Uart0_Info.DMA_CH_TX);
/*开启内部中断*/
nvic_irq_enable(DMA_Channel1_2_IRQn ,0);
/*6. USART DMA enable for transmission */
usart_dma_transmit_config(Uart0_Info.UartNumber, USART_DENT_ENABLE);
/* enable DMA channel1 transfer complete interrupt */ //开启DMA发送完成中断
dma_interrupt_enable(Uart0_Info.DMA_CH_TX, DMA_INT_FTF);
/*7. enable DMA channel1 */
dma_channel_enable(Uart0_Info.DMA_CH_TX);
/******************************以下DMA_USART0_RX配置******************************/
/*2.复位DMA的RX接收通道;*/
dma_deinit(Uart0_Info.DMA_CH_RX);
/*3.0 将DMA结构体中所有参数初始化为默认值*/
dma_struct_para_init(&dmaStruct);
/*3.1配置传输方向;*/
dmaStruct.direction = DMA_PERIPHERAL_TO_MEMORY;
/*3.2配置数据源地址;*/
dmaStruct.periph_addr = USART0_RDATA_ADDRESS;
/*3.3配置源地址是固定的还是增长的;*/
dmaStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
/*3.4配置源数据传输位宽;*/
dmaStruct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
/*3.5配置数据目的首地址;*/
dmaStruct.memory_addr = (uint32_t)Uart0_Comm.RecBuff_A;
/*3.6配置目的地址是固定的还是增长的;*/
dmaStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
/*3.7配置目的数据传输位宽;*/
dmaStruct.memory_width = DMA_MEMORY_WIDTH_8BIT;
/*3.8配置数据传输最大次数;*/
dmaStruct.number = RXD0_BUFF_SIZE;
/*3.9配置DMA通道优先级;DMA_PRIORITY_HIGH 改为最高ultra*/
dmaStruct.priority = DMA_PRIORITY_ULTRA_HIGH;
/*3.10 配置完后进行初始化*/
dma_init(Uart0_Info.DMA_CH_RX, &dmaStruct);
/*4.禁止DMA接收循环模式*/
dma_circulation_disable(Uart0_Info.DMA_CH_RX);
/*5.禁止DMA 内存到内存模式*/
dma_memory_to_memory_disable(Uart0_Info.DMA_CH_RX);
/*6.使能DMA的RX通道;*/
dma_channel_enable(Uart0_Info.DMA_CH_RX);
/*7. 使能UART接收数据使用DMA;*/
usart_dma_receive_config(Uart0_Info.UartNumber, USART_DENR_ENABLE);
}
/************************************************************************************************
**文 件 名:void Uart0_Init(uint32_t BaudRate)
**说 明:初始化串口
************************************************************************************************/
void Uart0_Init(uint32_t BaudRate)
{
Uart0_GPIO_Config();
Uart0_Comm_Config(BaudRate);
DMA_USART0_Init();
DIR_RX0(); //初始状态为接收
}
/************************************************************************************************
**文 件 名:DMA_Channel1_2_IRQHandler
**说 明:DMA发送完成中断服务函数
**作 用:主要用来给RS485切换方向:发送完成后,切换成接收模式
************************************************************************************************/
void DMA_Channel1_2_IRQHandler(void)
{
if (dma_interrupt_flag_get(Uart0_Info.DMA_CH_TX, DMA_INT_FLAG_FTF)) /*如果DMA发送完成标志置位*/
{
while (!usart_flag_get(Uart0_Info.UartNumber, USART_FLAG_TC) ); // 等待USART发送移位寄存器空(确保最后一字节已发出)
DIR_RX0(); /* 发送完成后,切换RS485为接收模式*/
dma_interrupt_flag_clear(Uart0_Info.DMA_CH_TX, DMA_INT_FLAG_FTF); // 清除DMA中断标志
}
}
/************************************************************************************************
**文 件 名:void USART0_IRQHandler(void)
**说 明:UART0接收空闲中断 即IDLE
************************************************************************************************/
void USART0_IRQHandler(void)
{
/*1.判断是不是IDLE产生的中断:如果空闲中断标志位置1*/
if(RESET != usart_interrupt_flag_get(Uart0_Info.UartNumber, USART_INT_FLAG_IDLE))
{
/*2. 清除中断标志位*/
usart_interrupt_flag_clear(Uart0_Info.UartNumber, USART_INT_FLAG_IDLE);
/*3. 读取收到的数据长度: 实际接收到的数据长度 = 接收缓冲区大小 - DMA没有传输的数量*/
Uart0_Comm.RecvBytes = RXD0_BUFF_SIZE - dma_transfer_number_get(Uart0_Info.DMA_CH_RX);
/*4.切换缓冲区*/
if(Uart0_Comm.Current_Rec_Buff == Uart0_Comm.RecBuff_A) //数据当前在RXD0_Buff_A中
{
Uart0_Comm.Current_Rec_Buff = Uart0_Comm.RecBuff_B; //切换成B缓冲区
}
else
{
Uart0_Comm.Current_Rec_Buff = Uart0_Comm.RecBuff_A; //如果为B就切换成A
}
/*5. 重启DMA指向另一缓冲区,对DMA搬运次数重新赋值,防止搬运次数用完,就不搬运了*/
dma_channel_disable(Uart0_Info.DMA_CH_RX); //设置之前需要关闭通道,否则不生效
dma_interrupt_flag_clear(Uart0_Info.DMA_CH_RX,DMA_INT_FLAG_FTF); //新增清除DMA接收完成标志
dma_memory_address_config(Uart0_Info.DMA_CH_RX, (uint32_t)Uart0_Comm.Current_Rec_Buff);
dma_transfer_number_config(Uart0_Info.DMA_CH_RX, RXD0_BUFF_SIZE); //重新设置搬运次数
dma_channel_enable(Uart0_Info.DMA_CH_RX); //设置完成后再打开
/*6.一帧标志位置位*/
Uart0_Comm.Frame_Rec_Flag = true;
//此时数据在非Current_Buff 中,数据长度为data_len,通过这个标志在主程序中处理即可
}
}
/***********************************************************/
int main(void)
{
/*串口0初始化*/
Uart0_Init(BSP_BAUD0); //波特率为115200u
while(1)
{
/*判断是否收到一帧数据,否则直接返回*/
if(Uart0_Comm.Frame_Rec_Flag == true)
{
Uar0_Process_Data();
Uart0_Comm.Frame_Rec_Flag =false;
}
}
return 0;
}
|
|
串口初始化时不允许使能 : usart_transmit_config(Uart0_Info.UartNumber, USART_TRANSMIT_ENABLE); //使能发送 。允许了发送就会申请DMA 喂它数据