前言 由于miniUSB的数据线的缺失,串口功能的体验使用外在的串口转USB工具进行。
目标 串口功能的实现。
分析 软件功能的实现的第一步就是要确保硬件连接正确,也就是RX和TX要和工具的串口连接正确,串口实现通信只要RX、TX和地连接上就可以,电不是必须的,逻辑正确就可以传输,软件功能的实现也是对硬件连接的进一步验证。使用的是PA9和PA10对应的UART0,和板载的资源使用的是同一个。
首先实现串口简单的发数,例如定期发送0x01,以此验证串口配置是否正确和硬件通路是否通畅。 串口配置,包括对应的IO口配置; - //******************************************************************************
- //* 函数名称 : gd_USART_init
- //* 函数描述 : 外部中断配置
- //* 输入参数 :
- //* 参数描述 : 外部中断初始化配置
- //* 输出参数 : 无
- //* 返回值 : 无
- //******************************************************************************
- void gd_USART_init(void)
- {
- /* enable COM GPIO clock */
- rcu_periph_clock_enable(RCU_GPIOA);
- /* enable USART clock */
- rcu_periph_clock_enable(RCU_USART0);
- /* connect port to USART TX */
- gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
- /* connect port to USART RX */
- gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
- /* configure USART TX as alternate function push-pull */
- gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
- gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
- /* configure USART RX as alternate function push-pull */
- gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
- gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
- /* USART configure */
- usart_deinit(USART0);
- usart_word_length_set(USART0, USART_WL_8BIT);
- usart_stop_bit_set(USART0, USART_STB_1BIT);
- usart_parity_config(USART0, USART_PM_NONE);
- usart_baudrate_set(USART0, 115200U);
- usart_receive_config(USART0, USART_RECEIVE_ENABLE);
- usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
- /* USART interrupt configuration */
- nvic_irq_enable(USART0_IRQn, 0);
- /* enable USART TBE interrupt */
- // usart_interrupt_enable(USART0, USART_INT_TBE);
- usart_enable(USART0);
- // usart_interrupt_flag_clear(USART0,USART_INT_FLAG_IDLE);
- // usart_interrupt_enable(USART0, USART_INT_IDLE);
- // USART0_DMA_Configuration();
- }
现象:
然后实现printf功能,我们在第一步已经实现了串口的通信,只要再配置printf重定向就可以了,定期发送字符串。 - int fputc(int ch, FILE *f)
- {
- usart_data_transmit(USART0, (uint8_t) ch);
- while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
- return ch;
- }
现象:
在体验了这两种发送方式后就需要来实现串口接收了,printf非常适合调试过程中打印输出调试信息,而在实际的交互过程中是需要使用usart_data_transmit直接进行个性化的发送模式设计。
串口接收为了实现不定长数据的接收处理,避免多次进入中断,准备采用DMA+串口空闲中断的方式,添加DMA配置,通过收到什么回传什么进行测试。DMA的通道映射参考DMAMUX 映射,可是我并没有找到通道请求表,类如GD32F350的这种:
Demo中的串口DMA并没有用到DMAMUX,但是DMAMUX是存在的,中间加了这么一道使DMA的应用更广泛,但是不用时的DMA对应的通道请求时什么样的呢? DMA配置代码: - void USART0_DMA_Configuration(void)
- {
- dma_parameter_struct dma_init_struct;
- /* enable DMA clock */
- rcu_periph_clock_enable(RCU_DMA);
- /* initialize DMA channel 1 */
- dma_deinit(DMA_CH1);
- dma_struct_para_init(&dma_init_struct);
- dma_init_struct.request = DMA_REQUEST_USART0_RX;
- dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
- dma_init_struct.memory_addr = (uint32_t)USART0RecePackBuf;
- dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
- dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
- dma_init_struct.number = DMA_USART0_LEN;
- dma_init_struct.periph_addr = (uint32_t)&USART_RDATA(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(DMA_CH1, &dma_init_struct);
- /* configure DMA mode */
- dma_circulation_disable(DMA_CH1);
- dma_memory_to_memory_disable(DMA_CH1);
- /* disable the DMAMUX_MUXCH1 synchronization mode */
- dmamux_synchronization_disable(DMAMUX_MUXCH1);
- /* USART DMA enable for reception */
- usart_dma_receive_config(USART0, USART_DENR_ENABLE);
- /* enable DMA channel 1 */
- dma_channel_enable(DMA_CH1);
- dma_flag_clear(DMA_CH1, DMA_FLAG_G);
- }
现象:在配置完成后会进入一次空闲中断
接下来就是简单的通信协议编制了,根据现有的资源只能是控制LED灯的操作了,目前有两种控制模式是通过按键完成的,现在通过通信协议把这两种控制方式写进去。 规定一下通信协议的格式: 改变展示模式,Data为对应的模式代号: 前导码 (STX) | 长度 | 命令 | 数据 | 校验和 | 8A F6 | 0x05 | 0x90 | 0x0E | Data | CHECKSUM |
例如:8A F6 05 90 0e 02 00 A0
控制呼吸灯,Data为对应的开关(0或者1): 前导码 (STX) | 长度 | 命令 | 数据 | 校验和 | 8A F6 | 0x05 | 0x91 | 0x0E | Data | CHECKSUM |
例如:8A F6 05 91 0e 01 00 A0
现象如下:
|