芯片选型
Ciga Device — GD32F470系列
串口通信
串口通信指的是通过串行通信接口进行数据传输的通信方式,通常用于短距离、低速率的数据传输。
串口通信可以使用不同的串行通信协议和接口,常见的串口通信协议有UART、USART、RS-232、RS-485、SPI、I2C等
UART通信介绍
Universal Asynchronous Receiver Transmitter 即 通用异步收发器,是一种通用的串行、异步通信总线 该总线有两条数据线,可以实现全双工的发送和接收 在嵌入式系统中常用于主机与辅助设备之间的通信。同步通信和异步通信的最大区别在于传输数据时是否需要时钟信号同步
UART的数据帧
发数据流程:
- 拉低电平
- 发送8位数据(先发低位,后发高位)
- 发校验位,校验位为0或1都可,也可以不发
- 再拉高电平代表结束
校验位
串口通信过程中有五种校验方式:奇校验(ODD)、偶校验(EVEN),1校验(MARK)、 0校验(SPACE),无校验(NONE)
- 奇校验(ODD):校验位被设置为确保数据位中1的总数为奇数。例如,数据位中的“1”总数为奇数,校验位被设置为低电平(拉低为0),否则设置为高电平。故而,如果接收方统计发现“1”总数为偶数,且校验是低电平,则校验失败,否则成功。
- 偶校验(Even): 校验位被设置为确保数据位中1的总数为偶数。例如,数据位中的“1”总数为偶数,校验位被设置为低电平(拉低为0),否则设置为高电平。故而,如果接收方统计发现“1”总数为奇数,且校验是低电平,则校验失败,否则成功。
- 1校验(MARK):1校验要求校验位始终为逻辑1,适用于古老的通讯设备。
- 0校验(SPACE):0校验要求校验位始终为逻辑0,也适用于古老的通讯设备。
- 无校验(NONE):不使用任何校验位,数据直接传输
注:部分ARM系列库要把发送的BIT长度word length
设置为9,才能正确发送校验位。
发送01和0011问题
发送01和0011,接收端如何来确定和区分
波特率
波特率决定了接收数据的速度,也就是每个电平持续的时间,用以区分是否是00或者11持续发送
累计误差消除
发送和接收的时候使用的是不同的时钟,所以即便是使用相同的波特率,也有可能因为时钟微小的不同步,造成数据的累计误差,解决方案是,UART只能一次性最多发送8bit的数据,然后重新发送,以消除累计误差
串口原理图
串口数据发送流程图
串口的标志位
代码实现步骤
- GPIO引脚配置(串口功能接硬件PIN脚,选择AF模式)
- 串口配置
- 串口发送单个字符函数函数
- 串口发送字符串函数
- 串口数据打印函数
- 串口接收数据函数
代码案例
Middleware/USAR0.h
#ifndef __UART_H__
#define __UART_H__
#define URAT0_RCU RCU_URAT0
#define UART0_GPIO_PERIPH GPIOA,
#define UART0_GPIO_PIN GPIO_PIN_9|GPIO_PIN_10
#include "gd32f4xx.h"
void uart_gpio_cfg(rcu_periph_enum rcu_periph,uint32_t gpio_periph, uint32_t gpio_pin, uint32_t alt_func_num);
void uart_init_cfg(rcu_periph_enum rcu_uart,uint32_t usart_periph,uint8_t nvic_irq);
//收到串口数据的回调函数
extern void USART0_on_recv(uint8_t *data,uint32_t len);
void USART0_sent_byte(uint8_t data);
void USART0_sent_data(uint8_t * data,uint32_t len);
void USART0_sent_string(char * data);
#endif
Middleware/USAR0.c
#include "uart.h"
#include <stdio.h>
void uart_gpio_cfg(rcu_periph_enum rcu_periph,uint32_t gpio_periph, uint32_t gpio_pin, uint32_t alt_func_num){
rcu_periph_clock_enable(rcu_periph);
gpio_mode_set(gpio_periph,GPIO_MODE_AF,GPIO_PUPD_NONE,gpio_pin);
gpio_output_options_set(gpio_periph,GPIO_OTYPE_PP,GPIO_OSPEED_MAX,gpio_pin);
gpio_af_set(gpio_periph,alt_func_num,gpio_pin);
}
void uart_init_cfg(rcu_periph_enum rcu_uart,uint32_t usart_periph,uint8_t nvic_irq){
rcu_periph_clock_enable(rcu_uart);
usart_deinit(usart_periph);
/* 波特率 configure usart baud rate value */
usart_baudrate_set(usart_periph, 115200U);
/* 奇偶校验configure usart parity function */
usart_parity_config(usart_periph, USART_PM_NONE);
/* 数据位configure usart word length */
usart_word_length_set(usart_periph, USART_WL_8BIT);
/* 停止位configure usart stop bit length */
usart_stop_bit_set(usart_periph, USART_STB_1BIT);
/* 开启发送configure USART transmitter */
usart_transmit_config(usart_periph, USART_TRANSMIT_ENABLE);
/* 开启接收configure USART receiver */
usart_receive_config(usart_periph, USART_RECEIVE_ENABLE);
/* 配置大小端模式,默认小端在前,小位先发,左边先收到。data is transmitted/received with the LSB/MSB first */
usart_data_first_config(usart_periph,USART_MSBF_LSB);
//开启NVIC中断 USART0_IRQn
nvic_irq_enable(nvic_irq,2,2);
//启用数据不为空的中断
usart_interrupt_enable(usart_periph,USART_INT_RBNE);
//启用接收完成的中断
usart_interrupt_enable(usart_periph,USART_INT_IDLE);
/* 启用UART enable usart */
usart_enable(usart_periph);
}
void USART0_sent_byte(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_sent_data(uint8_t * data,uint32_t len){
while(data&&len ){
USART0_sent_byte(*data);
//指针往后移一位
data++;
len--;
}
}
#define RX_BUFFER_LEN 1024
uint8_t g_rx_buffer[RX_BUFFER_LEN];
uint32_t g_rx_cnt=0;
void USART0_IRQHandler(){
if(SET == usart_interrupt_flag_get(USART0,USART_INT_FLAG_RBNE)){
// 接受到数据
printf("RBNE data");
// 清理标记
usart_interrupt_flag_clear(USART0,USART_INT_FLAG_RBNE);
// 获取寄存器的数据
uint8_t data = usart_data_receive(USART0);
g_rx_buffer[g_rx_cnt++]=data;
if(g_rx_cnt>=RX_BUFFER_LEN){
g_rx_cnt=0;
}
}
if(SET == usart_interrupt_flag_get(USART0,USART_INT_FLAG_IDLE)){
//空闲了
printf("IDLE data");
//读一次缓存池,可以清理IDLE的标记
usart_data_receive(USART0);
}
USART0_on_recv(g_rx_buffer,g_rx_cnt);
}
//printf打印
int fputc(int ch, FILE *f){
USART0_sent_byte((uint8_t)ch);
return ch;
}
void USART0_sent_string(char * data){
uint8_t i;
while(data && (*data)){
USART0_sent_byte((uint8_t)*data);
// 指针往下一个移动
data++;
}
}
User/main.c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "string.h"
#include "uart.h"
void USART0_on_recv(uint8_t *data,uint32_t len){
printf("recv:%s",data);
}
uint8_t arr[] = {'h', 'i', 'i', '\n'};
int main(void)
{
systick_config();
uart_gpio_cfg(RCU_GPIOA,GPIOA,GPIO_PIN_9,GPIO_AF_7);
uart_init_cfg(RCU_USART0,USART0,USART0_IRQn);
uint32_t cnt=0;
while(1) {
USART0_sent_byte(cnt++);
printf("hello\n");
USART0_sent_data(arr, 4);
// 发送字符串
USART0_sent_string("Hello world\n");
delay_1ms(500);
}
}