学习目标
- 掌握串口初始化流程
- 掌握串口接收逻辑
- 了解中断接收逻辑
-
学习内容
需求
串口数据接收
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_irqn = USART0_IRQn;
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_receive_config(usartx, USART_RECEIVE_ENABLE);
// 接收中断配置
nvic_irq_enable(usartx_irqn, 2, 2);
// usart int rbne
usart_interrupt_enable(usartx, USART_INT_RBNE);
usart_interrupt_enable(usartx, USART_INT_IDLE);
// 使能串口
usart_enable(usartx);
}
```c
define USART_RECEIVE_LENGTH 1024
//串口接收缓冲区大小 uint8_t g_recv_buff[USART_RECEIVE_LENGTH]; // 接收缓冲区 //接收到字符存放的位置 int g_recv_length = 0;
void USART0_IRQHandler(void) {
if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) == SET) {
uint16_t value = usart_data_receive(USART0);
g_recv_buff[g_recv_length] = value;
g_recv_length++;
}
if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == SET) {
//读取缓冲区,清空缓冲区
usart_data_receive(USART0);
g_recv_buff[g_recv_length] = ‘\0’;
// TODO: g_recv_buff为接收的数据,g_recv_length为接收的长度
g_recv_length = 0;
}
}
<a name="KBf1M"></a>
### 中断函数
1. 中断函数的名称是在CMSIS的汇编接口中定义的
![182.png](https://cdn.nlark.com/yuque/0/2023/png/27903758/1691665548744-8b682765-4a38-4990-b186-9b210ba3de40.png#averageHue=%23eeedec&clientId=u5bc733c2-08d2-4&from=ui&id=u37668828&originHeight=57&originWidth=1138&originalType=binary&ratio=1&rotation=0&showTitle=false&size=16241&status=done&style=none&taskId=ue855c04c-3a37-448d-a9e9-9a39443d69e&title=)<br />![183.png](https://cdn.nlark.com/yuque/0/2023/png/27903758/1691665563474-706cb574-625f-44f0-9fc6-68e3a8e1b86e.png#averageHue=%23042030&clientId=u5bc733c2-08d2-4&from=ui&id=ub995c6ab&originHeight=93&originWidth=971&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21904&status=done&style=none&taskId=u75c350a0-5f26-405f-a3f2-9cecc5ade85&title=)
2. 中断触发需要进行配置
```c
......
// 接收功能配置
usart_receive_config(usartx, USART_RECEIVE_ENABLE);
// 接收中断配置
nvic_irq_enable(usartx_irqn, 2, 2);
// usart int rbne
usart_interrupt_enable(usartx, USART_INT_RBNE);
usart_interrupt_enable(usartx, USART_INT_IDLE);
......
串口接收流程(了解)
寄存器与电路。
- 数据接收缓存寄存器(接收和发送其实公用一个寄存器)
- 状态寄存器
外部通过串口发送数据到MCU中来时,首先会把高低电平进行转换为单个byte,接着存储到这个缓存寄存器,存储一个byte的时候,会改变寄存器状态,然后会触发中断,我们在中断中,我们就知道接收到了一个byte,我们就可以去数据接收缓存寄存器中取数据,取完后,接收方又去存,这样周而复始的进行接收。知道外部不发送数据了,这个时候如果长期没有收到数据,就会触发闲置寄存器标记。
完整示例
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
/**
PA9 TXD
PA10 RXD
**/
#define USART_RECEIVE_LENGTH 1024
//串口接收缓冲区大小
uint8_t g_recv_buff[USART_RECEIVE_LENGTH]; // 接收缓冲区
//接收到字符存放的位置
int g_recv_length = 0;
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_irqn = USART0_IRQn;
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_receive_config(usartx, USART_RECEIVE_ENABLE);
// 接收中断配置
nvic_irq_enable(usartx_irqn, 2, 2);
// usart int rbne
usart_interrupt_enable(usartx, USART_INT_RBNE);
usart_interrupt_enable(usartx, USART_INT_IDLE);
// 使能串口
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));
}
void USART0_IRQHandler(void) {
if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) == SET) {
uint16_t value = usart_data_receive(USART0);
g_recv_buff[g_recv_length] = value;
g_recv_length++;
}
if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == SET) {
//读取缓冲区,清空缓冲区
usart_data_receive(USART0);
g_recv_buff[g_recv_length] = '\0';
// TODO: g_recv_buff为接收的数据,g_recv_length为接收的长度
printf("%s", g_recv_buff);
g_recv_length = 0;
}
}
//发送字符串
static void send_string(char *data){
//满足: 1.data指针不为空 2.发送的数据不是\0结束标记
while(data && *data){
send_data((uint8_t)(*data));
data++;
}
}
//重写fputc方法 调用printf,会自动调用这个方法实现打印
int fputc(int ch, FILE *f){
send_data((uint8_t)ch);
return ch;
}
int main(void)
{
systick_config();
USART_config();
while(1) {
}
}