文章目录[隐藏]
GD32F103串口DMA收发
这两年做嵌入式开发太难了,芯片缺货导致方案不断改改改,之前一直没用过国产MCU,从去年开始也渐渐了解了一下国产MCU。原理大同小异,资料相对来说确实少一点,生态也没那么完善。 这个系列打算把项目过程中的一些经验分享给大家,也作为自己的一个成长记录。如果能给路过的各位带来切实收获,请各位不吝点赞,给作者资以鼓励;如果文章中有错误,也请各位不吝指正,共同学习,共同进步!
串口DMA收发
废话不多说,直接上干货,我用的MCU是GD32F103CBT6,串口使用USART0,对应的GPIO是PA9和PA10。
1.串口IO初始化
代码如下:
static void usart_config(uint32_t baudval)
{
rcu_periph_clock_enable(RCU_GPIOA); //enable GPIO clock, PA9/PA10
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_USART0); //enable USART clock
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); //PA9--TX0
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); //PA10--RX0
//USART0: 115200
usart_deinit(USART0);
usart_baudrate_set(USART0, baudval);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_enable(USART0);
}
此处串口波特率采用传参方式在初始化串口时传入,实际使用时我用的串口波特率为115200。
2.DMA初始化
关于DMA通道映射详见UserManual 9.4.9 DMA请求映射及DMA各通道请求表。此处一定要仔细,不能搞错,我们使用的是USART0,由表中可知USART_TX对应的是Channel 3,USART_RX对应的是Channel 4。
代码如下:
uint8_t rxbuffer[2]= {'o','k'};
uint8_t txbuffer[10] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};
#define USART0_DATA_ADDRESS ((uint32_t)&USART_DATA(USART0))
static void usart_dma_config(void)
{
dma_parameter_struct dma_init_struct;
/* enable DMA0 clock */
rcu_periph_clock_enable(RCU_DMA0);
/* deinitialize DMA channel3(USART0 tx) */
dma_deinit(DMA0, DMA_CH3);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = NULL;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = ARRAYNUM(txbuffer);
dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
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_CH3, &dma_init_struct);
/* deinitialize DMA channel4 (USART0 rx) */
dma_deinit(DMA0, DMA_CH4);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)rxbuffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = ARRAYNUM(rxbuffer);;
dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.memory_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMA0, DMA_CH4, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH3);
dma_memory_to_memory_disable(DMA0, DMA_CH3);
dma_circulation_enable(DMA0, DMA_CH4);
dma_memory_to_memory_disable(DMA0, DMA_CH4);
/* USART DMA0 enable for reception */
usart_dma_receive_config(USART0, USART_DENR_ENABLE);
/* enable DMA0 channel4 transfer complete interrupt */
dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF);
/* enable DMA0 channel4 */
dma_channel_enable(DMA0, DMA_CH4);
/* USART DMA0 enable for transmission */
usart_dma_transmit_config(USART0, USART_DENT_ENABLE);
/* enable DMA0 channel3 transfer complete interrupt */
// dma_interrupt_enable(DMA0, DMA_CH3, DMA_INT_FTF);
// /* disable DMA0 channel3 */
// dma_channel_disable(DMA0, DMA_CH3);
}
3. 中断配置
串口接收采用中断方式,所以需要配置中断。
串口发送需要在主函数里根据项目实际需求进行发送,故不采用中断方式。
代码如下:
static void nvic_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE3_SUB1);
nvic_irq_enable(DMA0_Channel4_IRQn, 0, 1);
}
4. 串口初始化
代码如下:
void bsp_usart_init()
{
usart_config(BAUDRATE);
nvic_config();
usart_dma_config();
}
5. 串口发送函数和printf支持实现
代码如下:
void usart0_dma_send(uint8_t *buffer,uint16_t len)
{
dma_channel_disable(DMA0, DMA_CH3);
dma_memory_address_config(DMA0, DMA_CH3,(uint32_t)buffer);//设置要发送数据的内存地址
dma_transfer_number_config(DMA0, DMA_CH3, len);//一共发送多少个数据
dma_channel_enable(DMA0, DMA_CH3);
}
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART0, (uint8_t)ch);
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
return ch;
}
6. 头文件中的宏定义和函数声明如下
代码如下:
#define BAUDRATE 115200U
//#define BAUDRATE 9600U
#define ARRAYNUM(arr_name) (uint32_t)(sizeof(arr_name) / sizeof(*(arr_name)))
void bsp_usart_init(void);
void usart0_dma_send(uint8_t *buffer,uint16_t len);
int fputc(int ch, FILE *f);
7. 中断处理
串口接收中断里只需要清对应标志位即可。
void DMA0_Channel4_IRQHandler(void)
{
if(dma_interrupt_flag_get(DMA0, DMA_CH4, DMA_INT_FLAG_FTF)){
dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
}
}
8. 主函数
主函数里我们采用定时器+计数方式实现1s发送一次txbuffer的数据,当上位机发送数据给串口,数据通过DMA直接存到rxbuffer中。
extern uint8_t txbuffer[10];
extern uint8_t rxbuffer[2];
int main(void)
{
bsp_usart_init();
SysTick_Init();
bsp_wdg_init();
volatile uint8_t usartSend_taskTime = 0;
for(;;)
{
if(g_u8_taskTimer >= 10)
{
g_u8_taskTimer = g_u8_taskTimer - 10;
bsp_wdg_feed();
task_10msCycle();
usartSend_taskTime++;
if(usartSend_taskTime >= 100)
{
usart0_dma_send(txbuffer, (uint16_t)ARRAYNUM(txbuffer));
// printf("\n\r%s\n\r", rxbuffer);
usartSend_taskTime -=100;
}
}
}
}
9. 执行结果
rxbuffer和txbuffer中初始数据如下:
uint8_t rxbuffer[2]= {'o','k'};
uint8_t txbuffer[10] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};
上位机接收的串口数据:
rxbuff初始数据:
上位机发送字符"lk"后rxbuffer也随之改变:
至此,整个串口DMA收发测试成功。
总结
使用DMA进行数据传输的好处毋庸置疑,可以大大减少CPU的负担,CPU效率也会大大提升。
由上面步骤可知,串口用DMA收发主要分以下几步:
1)串口初始化
2)DMA初始化
3)中断配置
4)发送和接收数据
版权声明:本文为CSDN博主「嵌入式全栈工程师成长之路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/YoungNemoLu/article/details/122129130
暂无评论