采用GD32F103C8T6开发板实现了串口1和串口3的DMA空闲中断通信,在串口1上还添加了SP485,实现了485方向控制,
串口1的针脚为PA9和PA10
串口3的针脚为PB10和PB11,
由于在串口1添加了485模块,所以必须控制数据收发的方向控制,只有485模块的DE和RE为低电平时才能接收,高电平时才能发送,方向控制的针脚为PA8,实现代码如下:
/*
USART0
PA9:TX 发送
PA10:RX 接收
DMA_CH4为接收
DMA_CH3为发送
*/
#include <string.h>
#include "gd32f10x.h"
#include "systick.h"
#include "usart0_rs485.h"
uint8_t usart0_rx_buf[256];
uint8_t usart0_tx_buf[256];
volatile static uint8_t usart0_tx_busy = 0; //发送状态
volatile uint8_t usart0_rx_flag = 0; //接收状态
volatile uint16_t usart0_rx_len = 0; //接收长度
//若串口0添加了485,则需要配置485方向控制使能
#define rs485_rx_en GPIO_BC(GPIOA)=GPIO_PIN_8;
#define rs485_tx_en GPIO_BOP(GPIOA)=GPIO_PIN_8;
void USART0_nvic_config(void)
{
/* 设置优先级组 */
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
//USART0 DMA0发送中断
nvic_irq_enable(DMA0_Channel3_IRQn, 1, 0);
//USART0 DMA0接收中断
nvic_irq_enable(DMA0_Channel4_IRQn, 1, 0);
//USART0 串口中断
nvic_irq_enable(USART0_IRQn, 0, 0);
}
void USART0_rcu_config()
{
rcu_periph_clock_enable(RCU_DMA0);
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* enable USART clock */
rcu_periph_clock_enable(RCU_USART0);
rcu_periph_clock_enable(RCU_AF);
}
void USART0_dma_recv_config()
{
dma_parameter_struct dma_init_struct;
/* deinitialize DMA channel4 (USART0 rx) */
dma_deinit(DMA0, DMA_CH4);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)usart0_rx_buf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = sizeof(usart0_rx_buf);
dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART0);;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMA0, DMA_CH4, &dma_init_struct);
//dma_interrupt_enable(d->dma_periph, d->dma_rx_ch, DMA_INT_FTF);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH4);
/* enable DMA channel4 */
dma_channel_enable(DMA0, DMA_CH4);
}
void USART0_dma_send_config()
{
uint32_t dma = DMA0;
dma_channel_enum ch = DMA_CH3;
dma_parameter_struct ds;
uint32_t cpLen = sizeof(usart0_tx_buf);
/* 复位DMA参数结构体变量 */
dma_struct_para_init(&ds);
dma_deinit(dma, ch);
ds.direction = DMA_MEMORY_TO_PERIPHERAL;
ds.memory_addr = (uint32_t)usart0_tx_buf;
ds.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
ds.memory_width = DMA_MEMORY_WIDTH_8BIT;
ds.number = cpLen;
ds.periph_addr = (uint32_t)&USART_DATA(USART0);
ds.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
ds.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
ds.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(dma, ch, &ds);
dma_circulation_disable(dma, ch);
dma_memory_to_memory_disable(dma, ch);
dma_interrupt_enable(dma, ch, DMA_INT_FTF);
//dma_channel_enable(dma, ch);
usart_dma_transmit_config(dma, USART_TRANSMIT_DMA_ENABLE);
}
void USART0_dma_send(uint8_t *p, uint32_t number)
{
uint32_t dma = DMA0;
dma_channel_enum ch = DMA_CH3;
uint32_t cpLen = number;
if(cpLen > sizeof(usart0_tx_buf)){
cpLen = sizeof(usart0_tx_buf);
}
memcpy(usart0_tx_buf, p, cpLen);
//准备发送
rs485_tx_en;
//关闭DMA发送
dma_channel_disable(dma, ch);
//在发送地址改变的情况下需要重新配置发送地址
//dma_memory_address_config(dma, ch, (uint32_t)usart0_tx_buf);
//重新配置发送数据的长度
dma_transfer_number_config(dma, ch, number);
//启动DMA发送
dma_channel_enable(dma, ch);
}
void USART0_dma_recv_reconfig(void)
{
//uint32_t dma = DMA0;
//dma_channel_enum ch = DMA_CH4;
usart0_rx_flag = 0;
usart0_rx_len = 0;
memset(usart0_rx_buf,0,sizeof(usart0_rx_buf));
}
void USART0_Config(void)
{
uint32_t usart_periph = USART0;
//配置中断
USART0_nvic_config();
//配置时钟
USART0_rcu_config();
//使用PA8控制485的方向
gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP,ENABLE);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
rs485_rx_en;
/* connect port to USARTx_Tx PA9 */
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
/* connect port to USARTx_Rx PA10*/
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
/* USART configure */
usart_deinit(usart_periph);
usart_baudrate_set(usart_periph, 115200U);
usart_word_length_set(usart_periph, USART_WL_8BIT);
usart_stop_bit_set(usart_periph, USART_STB_1BIT);
usart_parity_config(usart_periph, USART_PM_NONE);
usart_receive_config(usart_periph, USART_RECEIVE_ENABLE);
usart_transmit_config(usart_periph, USART_TRANSMIT_ENABLE);
usart_hardware_flow_rts_config(usart_periph, USART_RTS_DISABLE); /* 无硬件流控制 */
usart_hardware_flow_cts_config(usart_periph, USART_CTS_DISABLE); /* 无硬件流控制 */
usart_dma_receive_config(usart_periph, USART_RECEIVE_DMA_ENABLE);
usart_dma_transmit_config(usart_periph, USART_TRANSMIT_DMA_ENABLE);
usart_enable(usart_periph);
//配置DMA
USART0_dma_recv_config();
//配置串口空闲中断和错误中断
usart_interrupt_enable(usart_periph, USART_INT_IDLE);
usart_interrupt_enable(usart_periph, USART_INT_ERR);
usart_interrupt_enable(usart_periph, USART_INT_PERR);
//配置传输完成中断
usart_interrupt_enable(usart_periph, USART_INT_TC);
USART0_dma_send_config();
}
void USART0_IRQHandler(void)
{
uint32_t usart_periph = USART0;
uint32_t dma = DMA0;
uint32_t err_flag = 0;
uint32_t flag = 0;
dma_channel_enum ch = DMA_CH4;
uint32_t stat = USART_STAT(USART0);
/* 统一错误处理(寄存器级) 性能影响:寄存器级操作比库函数更高效,但需确保位映射正确*/
if(stat & (USART_FLAG_ORERR | USART_FLAG_FERR | USART_FLAG_NERR | USART_FLAG_PERR)) {
uint16_t data = USART_DATA(USART0); // 关键清除操作
err_flag |= (stat & USART_FLAG_ORERR) ? 0x01 : 0;
err_flag |= (stat & USART_FLAG_NERR) ? 0x02 : 0;
err_flag |= (stat & USART_FLAG_FERR) ? 0x04 : 0;
err_flag |= (stat & USART_FLAG_PERR) ? 0x10 : 0;
}
if(RESET != usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_IDLE)) {
/* clear IDLE flag */
usart_interrupt_flag_clear(usart_periph, USART_INT_FLAG_IDLE);
usart_data_receive(usart_periph);
/* number of data received */
usart0_rx_len = sizeof(usart0_rx_buf) - (dma_transfer_number_get(dma, ch));
usart0_rx_flag = 1;
//关闭DMA接收
dma_channel_disable(dma, ch);
//重新配置接收数据的长度
dma_transfer_number_config(dma, ch, sizeof(usart0_rx_buf));
//重启DMA接收
dma_channel_enable(dma, ch);
}
/*传输完成中断
触发时机:满足以下条件同时成立时触发:
发送数据寄存器(TDR)为空
移位寄存器完成最后一bit数据的物理发送
*/
flag = USART_INT_FLAG_TC;
if(usart_interrupt_flag_get(usart_periph, flag)){
usart_interrupt_flag_clear(usart_periph, flag);
usart0_tx_busy = 0;
//传输完成后485启动接收使能,低电压
rs485_rx_en;
}
/* 错误恢复处理 */
if(err_flag){
usart_disable(usart_periph);
usart_enable(usart_periph); // 复位USART
}
}
//DMA0 ch4接收中断
void DMA0_Channel4_IRQHandler()
{
uint32_t usart_periph = USART0;
uint32_t dma = DMA0;
dma_channel_enum ch = DMA_CH4;
if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_FTF)) {
/* clear IDLE flag */
usart_data_receive(usart_periph);
usart0_rx_len = sizeof(usart0_rx_buf) - (dma_transfer_number_get(usart_periph, ch));
usart0_rx_flag = 1;
//清除接收完成中断
dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_FTF);
}
if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_ERR)) {
dma_channel_disable(dma, ch);
dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_ERR);
dma_channel_enable(dma, ch);
}
}
//USART0 DMA0 ch3发送中断
void DMA0_Channel3_IRQHandler(void)
{
uint32_t dma = DMA0;
dma_channel_enum ch = DMA_CH3;
//触发时机:当DMA传输计数器递减至0时自动触发,代表可以添加数据到缓冲区了
if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_FTF)){
dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_FTF);
}
if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_ERR)) {
dma_channel_disable(dma, ch);
dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_ERR);
dma_channel_enable(dma, ch);
}
}
void wait_usart0_send_finish(void)
{
while(usart0_tx_busy){};
}
void usart0_set_tx_busy(uint8_t busy)
{
usart0_tx_busy = busy;
}
uint8_t usart0_get_tx_busy(void)
{
return usart0_tx_busy;
}
源代码地址为:gd32f103c8t6-practice: 练习gd32f103c8t6开发板
购买的gd32f103c8t6开发板的链接为:
全新GD32F103C8T6开发板 含例程 含教学视频 GD32学习板核心板-淘宝网