学习目标
- 掌握串口初始化流程
- 掌握串口输出单个字符
- 掌握串口输出字符串
- 掌握通过串口printf
-
学习内容
需求
串口数据发送
添加Usart功能。
首先,选中Firmware,鼠标右键,点击Manage Project Items
接着,将gd32f4xx_usart.c
添加到依赖中
最后,观察Firmware
static void USART_config() {
/************** gpio config **************/
// 配置时钟
rcu_periph_clock_enable(RCU_GPIOA);
// 配置模式
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
// 配置复用功能
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
// 配置输出参数
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
/************** usart config **************/
// 串口时钟
rcu_periph_clock_enable(RCU_USART0);
// USART复位
usart_deinit(USART0);
// 波特率
usart_baudrate_set(USART0, 115200);
// 校验位
usart_parity_config(USART0, USART_PM_NONE);
// 数据位数
usart_word_length_set(USART0, USART_WL_8BIT);
// 停止位
usart_stop_bit_set(USART0, USART_STB_1BIT);
// 先发送高位还是低位
usart_data_first_config(USART0, USART_MSBF_LSB);
// 发送功能配置
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
// 使能串口
usart_enable(USART0);
}
//发送一byte数据
static void send_data(uint8_t data) {
//通过USART发送
usart_data_transmit(USART0, data);
//判断缓冲区是否已经空了
//FlagStatus state = usart_flag_get(USART_NUM,USART_FLAG_TBE);
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
}
//发送字符串
static void send_string(char *data){
//满足: 1.data指针不为空 2.发送的数据不是\0结束标记
while(data && *data){
send_data((uint8_t)(*data));
data++;
}
}
串口打印实现
int fputc(int ch, FILE *f){
send_data((uint8_t)ch);
return ch;
}
复用功能
串口发送流程(了解)
寄存器与电路。
数据发送缓存寄存器
- 状态寄存器
数据发送的流程,就是向发送缓冲区里放数据,这个发送缓存区寄存器只有有数据,就会触发电路,电路就按照这个数据模拟出高低电平往外发数据。
发送缓存区寄存器有个特点,小,只有一个byte,但是超快,寄存器在芯片内部寸土寸金。
存在一个问题,如果发送大量数据,这个寄存器的数据还没发送完成,会覆盖掉,这时候有一个状态寄存器记录了当前发送缓冲去是否是闲置的。(GD32是USART_FLAG_TBE
,STM32是UART_FLAG_TXE
)
关心的内容
将代码进行抽取
static void USART_config() {
uint32_t usartx_tx_rcu = RCU_GPIOA;
uint32_t usartx_tx_port = GPIOA;
uint32_t usartx_tx_pin = GPIO_PIN_9;
uint32_t usartx_tx_af = GPIO_AF_7;
uint32_t usartx_rx_rcu = RCU_GPIOA;
uint32_t usartx_rx_port = GPIOA;
uint32_t usartx_rx_pin = GPIO_PIN_10;
uint32_t usartx_rx_af = GPIO_AF_7;
uint32_t usartx = USART0;
uint32_t usartx_rcu = RCU_USART0;
uint32_t usartx_p_baudrate = 115200;
uint32_t usartx_p_parity = USART_PM_NONE;
uint32_t usartx_p_wl = USART_WL_8BIT;
uint32_t usartx_p_stop_bit = USART_STB_1BIT;
uint32_t usartx_p_data_first = USART_MSBF_LSB;
/************** gpio config **************/
// tx
rcu_periph_clock_enable(usartx_tx_rcu); // 配置时钟
gpio_mode_set(usartx_tx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, usartx_tx_pin);
gpio_af_set(usartx_tx_port, usartx_tx_af, usartx_tx_pin);
gpio_output_options_set(usartx_tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, usartx_tx_pin);
// rx
rcu_periph_clock_enable(usartx_rx_rcu); // 配置时钟
gpio_mode_set(usartx_rx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, usartx_rx_pin);
gpio_af_set(usartx_rx_port, usartx_rx_af, usartx_rx_pin);
gpio_output_options_set(usartx_rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, usartx_rx_pin);
/************** usart config **************/
// 串口时钟
rcu_periph_clock_enable(RCU_USART0);
// USART复位
usart_deinit(usartx);
usart_baudrate_set(usartx, usartx_p_baudrate); // 波特率
usart_parity_config(usartx, usartx_p_parity); // 校验位
usart_word_length_set(usartx, usartx_p_wl); // 数据位数
usart_stop_bit_set(usartx, usartx_p_stop_bit); // 停止位
usart_data_first_config(usartx, usartx_p_data_first); // 先发送高位还是低位
// 发送功能配置
usart_transmit_config(usartx, USART_TRANSMIT_ENABLE);
// 使能串口
usart_enable(usartx);
}
总结起来:
- GPIO引脚配置 ```c uint32_t usartx_tx_rcu = RCU_GPIOA; uint32_t usartx_tx_port = GPIOA; uint32_t usartx_tx_pin = GPIO_PIN_9; uint32_t usartx_tx_af = GPIO_AF_7;
uint32_t usartx_rx_rcu = RCU_GPIOA; uint32_t usartx_rx_port = GPIOA; uint32_t usartx_rx_pin = GPIO_PIN_10; uint32_t usartx_rx_af = GPIO_AF_7;
- **哪个引脚**
- **复用端口是哪个**
2. 串口配置
```c
uint32_t usartx = USART0;
uint32_t usartx_rcu = RCU_USART0;
uint32_t usartx_p_baudrate = 115200;
uint32_t usartx_p_parity = USART_PM_NONE;
uint32_t usartx_p_wl = USART_WL_8BIT;
uint32_t usartx_p_stop_bit = USART_STB_1BIT;
uint32_t usartx_p_data_first = USART_MSBF_LSB;
static void USART_config() { uint32_t usartx_tx_rcu = RCU_GPIOA; uint32_t usartx_tx_port = GPIOA; uint32_t usartx_tx_pin = GPIO_PIN_9; uint32_t usartx_tx_af = GPIO_AF_7;
uint32_t usartx_rx_rcu = RCU_GPIOA;
uint32_t usartx_rx_port = GPIOA;
uint32_t usartx_rx_pin = GPIO_PIN_10;
uint32_t usartx_rx_af = GPIO_AF_7;
uint32_t usartx = USART0;
uint32_t usartx_rcu = RCU_USART0;
uint32_t usartx_p_baudrate = 115200;
uint32_t usartx_p_parity = USART_PM_NONE;
uint32_t usartx_p_wl = USART_WL_8BIT;
uint32_t usartx_p_stop_bit = USART_STB_1BIT;
uint32_t usartx_p_data_first = USART_MSBF_LSB;
/************** gpio config **************/
// tx
rcu_periph_clock_enable(usartx_tx_rcu); // 配置时钟
gpio_mode_set(usartx_tx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, usartx_tx_pin);
gpio_af_set(usartx_tx_port, usartx_tx_af, usartx_tx_pin);
gpio_output_options_set(usartx_tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, usartx_tx_pin);
// rx
rcu_periph_clock_enable(usartx_rx_rcu); // 配置时钟
gpio_mode_set(usartx_rx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, usartx_rx_pin);
gpio_af_set(usartx_rx_port, usartx_rx_af, usartx_rx_pin);
gpio_output_options_set(usartx_rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, usartx_rx_pin);
/************** usart config **************/
// 串口时钟
rcu_periph_clock_enable(RCU_USART0);
// USART复位
usart_deinit(usartx);
usart_baudrate_set(usartx, usartx_p_baudrate); // 波特率
usart_parity_config(usartx, usartx_p_parity); // 校验位
usart_word_length_set(usartx, usartx_p_wl); // 数据位数
usart_stop_bit_set(usartx, usartx_p_stop_bit); // 停止位
usart_data_first_config(usartx, usartx_p_data_first); // 先发送高位还是低位
// 发送功能配置
usart_transmit_config(usartx, USART_TRANSMIT_ENABLE);
// 使能串口
usart_enable(usartx);
}
//发送一byte数据 static void send_data(uint8_t data) { //通过USART发送 usart_data_transmit(USART0, data); //判断缓冲区是否已经空了 //FlagStatus state = usart_flag_get(USART_NUM,USART_FLAG_TBE); while(RESET == usart_flag_get(USART0, USART_FLAG_TBE)); }
//发送字符串 static void send_string(char data){ //满足: 1.data指针不为空 2.发送的数据不是\0结束标记 while(data && data){ send_data((uint8_t)(*data)); data++; } }
int fputc(int ch, FILE *f){ send_data((uint8_t)ch); return ch; }
int main(void) { systick_config(); USART_config(); uint8_t cnt; while(1) { // send_data(cnt++); // send_string(“hello\r\n”); // printf(“hello %d\r\n”, cnt++); delay_1ms(1000); } } ```
如果按照如上代码配置还是不能输出日志,则需要勾选MicroLIB
练习题
- 实现串口数据发送
- 创建属于自己的串口发送模板代码