前言
GD32VF103_环境配置_GPIO_外部中断
GD32VF103_定时器中断
我们前两节写了GPIO, EXTI, TIMER的用例, 本节写串口用例, 发送, 接收中断, DMA, printf等. 仍然使用 GD32VF103C-START
板子, 注意板子上JP2要插跳线帽到Usart端(注意!注意!注意!, 不插发不出来信息):
用的串口0(USART0, PA9-TX, PA10-RX), 连接到板子上的CH340E到USB(CN1, 图中的上面的USB).
先安装CH340的驱动: http://www.wch.cn/products/CH340.html.
安装完后, 插上上面的USB(不能供电), 右边GD-Link的USB也要插上(板子供电需要), 可以在设备管理器中看到COM口.
新建工程
- File -> New -> C/C++ Project, 选择C Managed Build
- 选择GigaDevice RISC-V Project, 填入工程名
- MCU默认暂时只有GD32VF103, 然后Demo也没法选, 只有Running_LED, 不变
- 下一步, 完成.
串口发送
官方的库给了一个发送单uint32_t的函数 void usart_data_transmit(uint32_t usart_periph, uint32_t data)
, 我们这里用来发送一个字符数组和十六进制的数组:
#include "gd32vf103.h"
#include "systick.h"
void uart0_config(uint32_t baudrate) {
rcu_periph_clock_enable(RCU_GPIOA); //enable GPIO clock, PA9/PA10
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-8-1-N
usart_deinit(USART0);
usart_baudrate_set(USART0, baudrate);
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);
}
void usart0_transmit_arr(uint8_t arr[], uint32_t length) {
for(uint32_t i=0; i<length; i++) {
usart_data_transmit(USART0, arr[i]);
while (usart_flag_get(USART0, USART_FLAG_TBE)== RESET);
}
}
int main(void)
{
uart0_config(115200U);
uint8_t str_arr[] = "USART Example!\n\r";
uint8_t hex_arr[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
uint8_t arr_length = 0;
arr_length = sizeof(str_arr) / sizeof(*str_arr);
usart0_transmit_arr(str_arr, arr_length);
arr_length = sizeof(hex_arr) / sizeof(*hex_arr);
usart0_transmit_arr(hex_arr, arr_length);
while(1) {
}
return 0;
}
注意初始化 PA10--RX0
配置为 GPIO_MODE_IN_FLOATING
!
打开串口调试助手, 选中CH340的串口, 波特率设置 115200-8-1-N, 打开串口, 调试运行, 可以看到:
USART Example!
\0
切换hex显示:
55 53 41 52 54 20 45 78 61 6D 70 6C 65 21 0A 0D 00 01 02 03 04 05 06
添加printf支持
实测头文件添加 #include <stdio.h>
, 直接用printf
:
int main(void)
{
uart0_config(115200U);
printf("\n\rHello, GD32VF103!\n\r"); //\n\r, not \r\n
while(1) {
}
return 0;
}
当然, 官方给的printf例程添加了如下代码:
/* retarget the C library printf function to the USART */
int _put_char(int ch)
{
usart_data_transmit(USART0, (uint8_t)ch );
while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){
}
return ch;
}
放心些, 我们也找个地方把这段供起来.
串口中断
开全局中断, 设置组优先级, 配置串口, 开启串口 USART_INT_RBNE
中断, RBNE: read data buffer not empty interrupt
.
接收中断一个字节中断一次, 存到数组, 存满就发给上位机.
代码如下:
#define RX_BUF_SIZE 10U
uint8_t rxbuffer[RX_BUF_SIZE];
__IO uint16_t rxcount = 0;
__IO FlagStatus g_receive_complete = RESET;
int main(void)
{
/* USART interrupt configuration */
eclic_global_interrupt_enable();
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
eclic_irq_enable(USART0_IRQn, 1, 0);
uart0_config(115200U);
printf("USART RECEIVE INTERRUPT EXAMPLE:\n\r");
usart_interrupt_enable(USART0, USART_INT_RBNE);
while(1) {
if(g_receive_complete == SET) {
rxcount = 0;
g_receive_complete = RESET;
usart_interrupt_enable(USART0, USART_INT_RBNE);
usart0_transmit_arr(rxbuffer, RX_BUF_SIZE);
}
}
return 0;
}
void USART0_IRQHandler(void) {
if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)){
/* receive data */
rxbuffer[rxcount++] = usart_data_receive(USART0);
if(rxcount == RX_BUF_SIZE){
usart_interrupt_disable(USART0, USART_INT_RBNE);
g_receive_complete = SET;
}
}
}
调试运行, 我们每1s发送 0123456789
, 可以看到原封不动返回:
事实上, 我测试了定时1ms发送01234567890
, 10万字节不丢数.
DMA
上面的接收中断一个字节中断一次, 我们可以设置DMA接收, 接收到指定数量的字节再触发一次中断, 这样可以减轻下CPU的负担.
下面的代码中, 使用DMA接收方式接收 abcdefghijklmnopqrstuvwxyz
26个英文字母加上回车换行共28个字节, 接收完一帧就传给上位机, DMA初始配置中开启了循环接收 dma_circulation_enable(DMA0, DMA_CH4);
, 所以会自动开始. 代码如下:
#define RX_DMA_BUF_SIZE 28U
uint8_t rx_dma_buffer[RX_DMA_BUF_SIZE];
__IO FlagStatus g_dma_receive_complete = RESET;
void uart0_dma_config(uint8_t buffer[], uint32_t number) {
dma_parameter_struct dma_init_struct;
rcu_periph_clock_enable(RCU_DMA0); // enable DMA0 clock first !!!!!!!!!
eclic_global_interrupt_enable(); /*configure DMA0 interrupt*/
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
eclic_irq_enable(DMA0_Channel4_IRQn, 2, 0);
/* 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)buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = number; //modify this number
dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART0);
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_CH4);
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);
}
int main(void) {
uart0_config(115200U);
uart0_dma_config(rx_dma_buffer, RX_DMA_BUF_SIZE);
printf("USART printf and DMA interrupt receive example:\n\r");
while(1) {
if(g_dma_receive_complete == SET) {
g_dma_receive_complete = RESET;
usart0_transmit_arr(rx_dma_buffer, RX_DMA_BUF_SIZE);
}
}
return 0;
}
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);
g_dma_receive_complete = SET;
// for(uint32_t i = 0; i < RX_DMA_BUF_SIZE; i++) {
// usart_data_transmit(USART0, (uint8_t)(rx_dma_buffer[i]));
// while (usart_flag_get(USART0, USART_FLAG_TBE)== RESET);
// }
}
}
先清掉开始的printf, 我们10ms循环发送, 可以看到26万字节不丢数: