六、USART1发送和接收的DMA实现
<p>在下的GD32L233C-START专题测评:</p><p>一、开箱评测 <a href="https://bbs.eeworld.com.cn/thread-1192788-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1192788-1-1.html</a></p>
<p>二、GD32L233C-START环境搭建<a href="https://bbs.eeworld.com.cn/thread-1193053-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193053-1-1.html</a></p>
<p>三、点灯案例与扩展<a href="https://bbs.eeworld.com.cn/thread-1193183-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193183-1-1.html</a></p>
<p>四、Demo工程的分析<a href="https://bbs.eeworld.com.cn/thread-1193279-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193279-1-1.html</a></p>
<p>五、USART0的printf实现和DMA实现<a href="https://bbs.eeworld.com.cn/thread-1193517-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193517-1-1.html</a></p>
<hr />
<p>开学后,本人变得忙碌了,到了周末再发一篇,继续研究串口DMA,这回拿USART1来实现,并为后面连接ESP8266 AT模块做好准备。</p>
<p>1、案例思路</p>
<p> 本例以GD32L233C-START之USART1口的发送、接收功能实现,外加DMA方式作为目标,在工程中添加了Bsp目录,并添加自己的串口驱动文件“aita_gd32l233_usart.c”和“aita_gd32l233_usart.h”(当然,这是源于个人习惯,写在main.c里也一样)。</p>
<p> 头文件中,增加宏定义,用于条件编译,控制DMA发送和DMA接收的开启。</p>
<p> 源文件中,因为是DMA方式,定义了发送和接收缓存区,接收缓冲区索引量,发送中标志量。另外,为了在多个函数中可以配置DMA,定义了全局的“dma_parameter_struct(DMA参数配置)”结构体变量。</p>
<p> USART1初始化与DMA初始化分别写成两个函数,由宏控制USART1初始化函数对DMA初始化函数的调用。发送功能函数也有宏来控制,分为开启DMA方式和不开启方式。DMA发送中判定标志量,以防止调用冲突。</p>
<p> gd32l23x_it.c中,增加USART1中断函数和DMA通道2(用于发送)及通道3(用于接收)中断函数。USART1_IRQHandler()中,启用DMA时进行“空闲中断”检测以保证有接收即反应(否则是DMA缓存区收满才有反应),不启用时进行“接收中断”检测以实现收到字节即存入缓冲区。</p>
<p>2、代码实现</p>
<p> “..\GD32L233C-START\GD32L233C_START_Demo_Suites\Projects\AITA_GD32L233C\Bsp\inc\aita_gd32l233_usart.h”(本人建立的路径)代码:</p>
<pre>
<code>#ifndef _AITA_USART_H
#define _AITA_USART_H
//控制DMA是否启用
#define AITA_DMA_U1Tx_EN 1//开启UART1口的DMA发送
#define AITA_DMA_U1Rx_EN 1//开启UART1口的DMA接收
//DMA USART1 发送和接收寄存器地址
#define USART1_RDATA_ADDRESS (&USART_RDATA(USART1))
#define USART1_TDATA_ADDRESS (&USART_TDATA(USART1))
//USART1接收和发送缓存长度
#define USART1_BUF_LENGTH 128
#endif</code></pre>
<p> </p>
<p> “..\GD32L233C-START\GD32L233C_START_Demo_Suites\Projects\AITA_GD32L233C\Bsp\inc\aita_gd32l233_usart.c”(本人建立的路径)代码:</p>
<pre>
<code>#include "aita_sys.h"
//DMA USART1 用到的内存缓存
uint8_t u1txbuffer;
uint8_t u1rxbuffer;
//接收缓存索引
uint16_t u1rxindex = 0;
//发送中标志(用于DMA发送)
uint8_t u1txdoing = 0;
//全局DMA参数配置结构体变量
dma_parameter_struct dma_init_struct;
void aita_InitU1(void)
{
/* enable COM GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* enable USART clock */
rcu_periph_clock_enable(RCU_USART1);
/* connect port to USART TX */
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_2);
/* connect port to USART RX */
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_3);
/* configure USART TX as alternate function push-pull */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);
/* configure USART RX as alternate function push-pull */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_3);
/* USART configure */
usart_deinit(USART1);
usart_word_length_set(USART1, USART_WL_8BIT);
usart_stop_bit_set(USART1, USART_STB_1BIT);
usart_parity_config(USART1, USART_PM_NONE);
usart_baudrate_set(USART1, 115200U);
usart_receive_config(USART1, USART_RECEIVE_ENABLE);
usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
nvic_irq_enable(USART1_IRQn, 0);
//启用Usart1 Rx DMA则开启空闲中断,否则开启接收中断
#if !AITA_DMA_U1Rx_EN
usart_interrupt_enable(USART1, USART_INT_RBNE);
#else
usart_interrupt_enable(USART1, USART_INT_IDLE);
#endif
usart_enable(USART1);
aita_InitDMAForU1();
}
void aita_InitDMAForU1(void) {
#if AITA_DMA_U1Tx_EN || AITA_DMA_U1Rx_EN
/* enable DMA clock */
rcu_periph_clock_enable(RCU_DMA);
#endif
#if AITA_DMA_U1Tx_EN
/*configure DMA interrupt*/
nvic_irq_enable(DMA_Channel2_IRQn, 0); //DMA CH2 for Uart1 Tx
/* initialize DMA channel 2 */
dma_deinit(DMA_CH2);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.request = DMA_REQUEST_USART1_TX;
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr= (uint32_t)u1txbuffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = ARRAYNUM(u1txbuffer);
dma_init_struct.periph_addr= (uint32_t)USART1_TDATA_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(DMA_CH2, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA_CH2);
dma_memory_to_memory_disable(DMA_CH2);
/* disable the DMAMUX_MUXCH2 synchronization mode */
dmamux_synchronization_disable(DMAMUX_MUXCH2);
/* USART DMA enable for transmission and reception */
usart_dma_transmit_config(USART1, USART_DENT_ENABLE);
/* enable DMA channel 2 transfer complete interrupt */
dma_interrupt_enable(DMA_CH2, DMA_INT_FTF);
/* disable DMA channel 2,每次发送时临时开启DMA */
dma_channel_disable(DMA_CH2);
#endif
#if AITA_DMA_U1Rx_EN
/*configure DMA interrupt*/
nvic_irq_enable(DMA_Channel3_IRQn, 0); //DMA CH3 for Uart1 Rx
/* initialize DMA channel 3 */
dma_deinit(DMA_CH3);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.request = DMA_REQUEST_USART1_RX;
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr= (uint32_t)u1rxbuffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = ARRAYNUM(u1rxbuffer);
dma_init_struct.periph_addr= (uint32_t)USART1_RDATA_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(DMA_CH3, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA_CH3);
dma_memory_to_memory_disable(DMA_CH3);
/* disable the DMAMUX_MUXCH3 synchronization mode */
dmamux_synchronization_disable(DMAMUX_MUXCH3);
/* USART DMA enable for reception */
usart_dma_receive_config(USART1, USART_DENR_ENABLE);
/* enable DMA channel 3 transfer complete interrupt */
dma_interrupt_enable(DMA_CH3, DMA_INT_FTF);
/* enable DMA channel 3, 接收是被动的所以DMA要一直开启 */
dma_channel_enable(DMA_CH3);
#endif
}
void aita_U1SendStr(uint8_t *arr, uint16_t len) {
#if AITA_DMA_U1Tx_EN //DMA发送模式,加载缓冲区,开启通道
while(u1txdoing);
memcpy(u1txbuffer, arr, len);
aita_U1DMASend(len);
#else //非DMA模式,程序查询,操作USART1_DR
uint16_t i=0;
while(i < len) {
usart_data_transmit(USART1, arr);
while(RESET == usart_flag_get(USART1, USART_FLAG_TBE));
}
#endif
}
void aita_ClearU1RxBuffer(void) {
memset(u1rxbuffer, 0, USART1_BUF_LENGTH);
u1rxindex = 0;
}
#if AITA_DMA_U1Tx_EN
void aita_U1DMASend(uint16_t size) {
u1txdoing = 1;
dma_transfer_number_config(DMA_CH2, size);
dma_channel_enable(DMA_CH2);
}
#endif
</code></pre>
<p> </p>
<p> 也是建立的系统头文件aita_sys.h中,新增(主要是变量声明)代码:</p>
<pre>
<code>#include "aita_gd32l233_usart.h"
extern uint8_t rx_buffer;
extern uint32_t nbr_data_to_read;
extern volatile uint32_t rx_counter;
//DMA USART1 用到的内存缓存
extern uint8_t u1txbuffer;
extern uint8_t u1rxbuffer;
extern uint16_t u1rxindex;
extern uint8_t u1txdoing;
extern dma_parameter_struct dma_init_struct;</code></pre>
<p> </p>
<p> gd32l23x_it.c中建立的中断函数代码:</p>
<pre>
<code>void USART1_IRQHandler(void)
{
#if !AITA_DMA_U1Rx_EN
//未启用U1接收DMA,则采用接收中断
if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
//如果缓冲区填满,则清空
if(u1rxindex > USART1_BUF_LENGTH) {
u1rxindex = 0;
memset(u1rxbuffer, 0, USART1_BUF_LENGTH);
}
/* read one byte from the receive data register */
u1rxbuffer = (uint8_t)usart_data_receive(USART1);
}
#else
if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
dma_channel_disable(DMA_CH3); //串口空闲后,关闭接收DMA防止干扰
u1rxindex = USART1_BUF_LENGTH
- dma_transfer_number_get(DMA_CH3);//获取接收到的字节数
dma_transfer_number_config(DMA_CH3, USART1_BUF_LENGTH);
dma_channel_enable(DMA_CH3);//再开启接收DMA
}
#endif
}
#if AITA_DMA_U1Tx_EN
void DMA_Channel2_IRQHandler(void)
{
if(RESET != dma_interrupt_flag_get(DMA_CH2, DMA_INT_FLAG_FTF)){
dma_interrupt_flag_clear(DMA_CH2, DMA_INT_FLAG_G);
dma_channel_disable(DMA_CH2);
u1txdoing = 0;
}
}
#endif
#if AITA_DMA_U1Rx_EN
//Usart1 Rx DMA ISR(应当永不触发)
void DMA_Channel3_IRQHandler(void)
{
if(RESET != dma_interrupt_flag_get(DMA_CH3, DMA_INT_FLAG_FTF)){
dma_interrupt_flag_clear(DMA_CH3, DMA_INT_FLAG_G);
dma_channel_disable(DMA_CH3);
dma_transfer_number_config(DMA_CH3, USART1_BUF_LENGTH);
dma_channel_enable(DMA_CH3);
}
}
#endif</code></pre>
<p> </p>
<p> 最后是main.c中 编写的测试函数(这个很简单,仅是收发测试):</p>
<pre>
<code>void aita_TestU1(void) {
while(1) {
aita_U1SendStr("hello from usart1 in dma\r\n", sizeof("hello from usart1 in dma\r\n"));
if(u1rxindex) {
aita_U1SendStr("Rsv: ", sizeof("Rsv: "));
aita_U1SendStr(u1rxbuffer, u1rxindex);
aita_U1SendStr("\r\n", sizeof("\r\n"));
aita_ClearU1RxBuffer();
}
delay_1ms(1000);
}
}</code></pre>
<p> </p>
<p>你好 ,能上传测试结果吗 ?<br />
我参考了你的测试没反应呢。</p>
罂粟花开何时醉 发表于 2023-5-17 02:22
你好 ,能上传测试结果吗 ?
我参考了你的测试没反应呢。
<p>很久前做的了,现在不太方便复现了。不要意思。</p>
页:
[1]