立创·天空星开发板-GD32F407VE-USART

本文以 立创·天空星开发板-GD32F407VET6-青春版 作为学习的板子,记录学习笔记。

立创·天空星开发板-GD32F407VE-USART

    • 基础通信概念
      • 同步通信 & 异步通信
      • 串行通信 & 并行通信
      • 双工 & 单工
      • 通讯速率
        • 码元
    • 串口通信
      • 数据帧
    • 串口封装

基础通信概念

通信协议是网络中(包括互联网)设备之间交换信息时所必须遵守的规则的正式描述。

同步通信 & 异步通信

  • 分类依据:通信过程中发送方和接收方是否使用相同的时钟频率。
  • 同步通信(Synchronous Communication):发送方和接收方使用相同的时钟频率,数据以比特流(数据帧)的形式连续传输。数据帧包括同步字符、数据部分和校验码等。
  • 异步通信(Asynchronous Communication):发送方和接收方使用各自的时钟频率,数据以字符为单位进行传输。每个字符数据前有一个起始位,后有停止位,用于标识字符的开始和结束。

故而,同步通信和异步通信的最大区别在于传输数据时是否需要时钟信号同步。有钟为同,无钟为异。

串行通信 & 并行通信

  • 分类依据:数据在传输线上的传输方式。
  • 串行通信(Serial Communication):数据的各个位在同一根数据线上逐位发送和接收。串行通信传输速度慢,但传输距离远,适合远距离通信。
  • 并行通信(Parallel Communication):数据的各个位同时在多根数据线上发送或接收。并行通信传输速度快,但成本高,适合短距离通信。

故而,串行通信和并行通信的最大区别在于传输数据时数据线的根数。单根为串,多根为并。

双工 & 单工

  • 分类依据:通信双方的信息传输方向。
  • 双工(Duplex)
    • 全双工(Full-Duplex):通信双方可以同时发送和接收数据。
    • 半双工(Half-Duplex):通信双方可以相互发送和接收数据,但不能同时进行。
  • 单工(Simplex):通信是单向的,一方只能发送数据,另一方只能接收数据。

故而,双工和单工的最大区别在于传输方向是否是双向的。双传为双工,单传为单工。

通讯速率

衡量通讯性能的一个非常重要的参数就是通讯速率

  • 比特率:每秒钟传输的二进制位数,单位为(bit/s)
  • 波特率:每秒钟传输了多少个码元
码元

码元是通讯信号调制的概念,通讯中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。

如果在通讯传输中,用 0V 表示数字 0,5V 表示数字 1,那么一个码元可以表示两种状态 0 和 1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致。

如果在通讯传输中,有 0V、2V、4V 以及 6V 分别表示二进制数 00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。

因为很多常见的通讯中一个码元都是表示两种状态(0和1),所以常常直接以波特率来表示比特率。

串口通信

Universal Asynchronous Receiver Transmitter 即 : 通用异步收发器

串口通信是一种通用的串行的异步通信总线,该总线有两条数据线,可以实现全双工的发送和接收,在嵌入式系统中常用于主机与辅助设备之间的通信。

数据帧

  • 在串口通信中,数据帧是传输数据的单元。
  • 一帧数据包含多个组成部分,以确保数据的正确传输和接收。
    数据帧
    以传递一个字母 k 为例,若数据为采用八位,并使用低位先行的方式进行传输。过程大致如下:
  • 字母 k 的二进制为 01101011
  • 采用低位先行的传输方式的话,其传输顺序应该是 11010110
  • 传输波形大致如下所示:
    串口数据发送波形

串口封装

天空星开发板的 PA9 和 PA10 对应的是 USART0 的 TX 和 RX 功能。
运用库函数,封装代码如下:

  • ExtendedUSART.h
#ifndef __EXTENDED_USART_H__
#define __EXTENDED_USART_H__

#include "gd32f4xx.h"
#include <stdio.h>

// 发送配置
#define USART_TX_RCU        RCU_GPIOA
#define USART_TX_PORT       GPIOA
#define USART_TX_PIN        GPIO_PIN_9
#define USART_TX_ALT        GPIO_AF_7
// 接收配置
#define USART_RX_RCU        RCU_GPIOA
#define USART_RX_PORT       GPIOA
#define USART_RX_PIN        GPIO_PIN_10
#define USART_RX_ALT        GPIO_AF_7
// 中断配置
#define USART_RCU           RCU_USART0
#define USART_NUM           USART0
#define USART_IRQ           USART0_IRQn
#define USART_IRQ_Handler   USART0_IRQHandler

/*!
    \brief    初始化串口 0
    \param[in]  b  : [uint8_t] 一个字节的数据
    \param[out] none
    \retval     none
*/
void USART0_config();

/*!
    \brief    往串口 0 发送1个字节数据
    \param[in]  b  : [uint8_t] 一个字节的数据
    \param[out] none
    \retval     none
*/
void USART0_send_byte(uint8_t b);

/*!
    \brief    往串口 0 发送多个字节数据
    \param[in]  dat  : [uint8_t*] 字节数据首元素地址
    \param[in]  len  : [uint32_t] 字节数据的长度
    \param[out] none
    \retval     none
*/
void USART0_send_data(uint8_t* dat, uint32_t len);

/*!
    \brief    往串口 0 发送发送字符串 (结尾标记\0)
    \param[in]  dat  : [char*] 字符串首字符地址
    \param[out] none
    \retval     none
*/
void USART0_send_string(char *dat);

#endif
  • ExtendedUSART.c
#include "ExtendedUSART.h"


// 串口接收缓冲区大小
#define USART_RECEIVE_LENGTH  1024
// 串口接收数据缓冲区
static uint8_t g_recv_buff[USART_RECEIVE_LENGTH];
// 接收到字符的长度
static uint32_t g_recv_length = 0;


/*!
    \brief    初始化串口 0
    \param[in]  b  : [uint8_t] 一个字节的数据
    \param[out] none
    \retval     none
*/
void USART0_config() {
    // GPIO 配置: TXD, PA9
    rcu_periph_clock_enable(USART_TX_RCU);
    gpio_mode_set(USART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, USART_TX_PIN);
    gpio_af_set(USART_TX_PORT, USART_TX_ALT, USART_TX_PIN);
    // GPIO 配置: TXD, PA10
    rcu_periph_clock_enable(USART_RX_RCU);
    gpio_mode_set(USART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, USART_RX_PIN);
    gpio_af_set(USART_RX_PORT, USART_RX_ALT, USART_RX_PIN);

    // 启用串口时钟
    rcu_periph_clock_enable(USART_RCU);
    // 复位串口
    usart_deinit(USART_NUM);

    usart_baudrate_set(USART_NUM, 115200);	            // 波特率
    usart_parity_config(USART_NUM, USART_PM_NONE);      // 校验位, 无校验(USART_PM_NONE), 偶校验(USART_PM_EVEN), 奇校验(USART_PM_ODD)
    usart_word_length_set(USART_NUM, USART_WL_8BIT);    // 数据位数
    usart_stop_bit_set(USART_NUM, USART_STB_1BIT);      // 停止位
    usart_data_first_config(USART_NUM, USART_MSBF_LSB); // 先发送高位还是低位

    // 发送功能配置
    usart_transmit_config(USART_NUM, USART_TRANSMIT_ENABLE);
	
    // 接收功能配置
    usart_receive_config(USART_NUM, USART_RECEIVE_ENABLE);
    // 接收中断配置
    nvic_irq_enable(USART_IRQ, 2, 2);
    // 串口中断能
    usart_interrupt_enable(USART_NUM, USART_INT_RBNE);
    usart_interrupt_enable(USART_NUM, USART_INT_IDLE);
	
    // 串口使能
    usart_enable(USART_NUM);
}

/*!
    \brief    往串口 0 发送1个字节数据
    \param[in]  b  : [uint8_t] 一个字节的数据
    \param[out] none
    \retval     none
*/
void USART0_send_byte(uint8_t b) {
    //通过USART发送
    usart_data_transmit(USART0, b);
    //判断缓冲区是否已经空了
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
}

/*!
    \brief    往串口 0 发送多个字节数据
    \param[in]  dat  : [uint8_t*] 字节数据首元素地址
    \param[in]  len  : [uint32_t] 字节数据的长度
    \param[out] none
    \retval     none
*/
void USART0_send_data(uint8_t* dat, uint32_t len) {
    while(dat && len--) {
        USART0_send_byte(*dat);
        dat++;
    }
}

/*!
    \brief    往串口 0 发送发送字符串 (结尾标记\0)
    \param[in]  dat  : [char*] 字符串首字符地址
    \param[out] none
    \retval     none
*/
void USART0_send_string(char *dat) {
    //满足: 1.data指针不为空  2.发送的数据不是\0结束标记
    while(dat && *dat) {
        USART0_send_byte((uint8_t)(*dat));
        dat++;
    }
}

int fputc(int ch, FILE *f) {
    USART0_send_byte((uint8_t)ch);
    return ch;
}

// 声明弱函数, 用于接收来自串口 0 的数据并回显
// 外部可以通过声明同名强函数来覆盖该函数的默认行为
__attribute__((weak)) void USART0_on_received(uint8_t* dat, uint32_t len) {
	USART0_send_data(dat,len);
}

// 串口接收数据的中断处理函数
void USART_IRQ_Handler(void) {
    if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) == SET) {
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
        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';

        // g_recv_buff 为接收的数据, g_recv_length 为接收的长度
        USART0_on_received(g_recv_buff, g_recv_length);

        g_recv_length = 0;
    }
}
  • main.c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"

#include "ExtendedUSART.h"

void USART0_on_received(uint8_t* dat, uint32_t len) {
	printf("Received: %s\r\n", dat);
}

int main(void) {
    systick_config();
    USART0_config();
	
    uint8_t cnt = 0;
    while(1) {
        USART0_send_byte(cnt++);
        USART0_send_byte(0x0D); // \r
        USART0_send_byte(0x0A); // \n
        USART0_send_string("This is string!\r\n");
        printf("cnt = %d\r\n", cnt);
        if(cnt > 99) {
        	cnt = 0;
        }
        delay_1ms(1000);
    }
}

额外说明

  1. 如果希望通过 printf 函数将数据打印到串口助手上显示,需要重写 stdio.h 中的 fputc 函数。
  2. 如果希望调用方无论是否定义回调函数都能使得回调函数默认可用,可以采用定义弱函数的方式来实现。也就是我这边提供的 ExtendedUSART.c 文件中的 USART0_on_received 函数的定义方式,即便是在 main.c 中没有定义,照样能回显数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/688343.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

本地运行ChatTTS

TTS 是将文字转为语音的模型&#xff0c;最近很火的开源 TTS 项目&#xff0c;本地可以运行&#xff0c;运行环境 M2 Max&#xff0c;差不多每秒钟 4&#xff5e;&#xff5e;5 个字。本文将介绍如何在本地运行 ChatTTS。 下载源码 首先下载源代码 git clone https://github…

WPF中读取Excel文件的内容

演示效果 实现方案 1.首先导入需要的Dll(这部分可能需要你自己搜一下) Epplus.dll Excel.dll ICSharpCode.SharpZipLib.dll 2.在你的解决方案的的依赖项->添加引用->浏览->选择1中的这几个Dll点击确定。(添加依赖) 3.然后看代码内容 附上源码 using Excel; usi…

TypeScript环境安装与VScode编辑器的使用

说明大背景环境&#xff0c;我用的是window10系统。 1.安装node.js 。 去官网下载安装包。 虽然我去的是官网&#xff0c;但是不知为何下载了个不知名的东西&#xff0c;后来又找了个链接才下载正确了。 实际上就是一个.msi的文件。我用的版本&#xff1a;node-v18.19.0-x6…

【第四节】C/C++数据结构之树与二叉树

目录 一、基本概念与术语 二、树的ADT 三、二叉树的定义和术语 四、平衡二叉树 4.1 解释 4.2 相关经典操作 4.3 代码展示 一、基本概念与术语 树(Tree)是由一个或多个结点组成的有限集合T。其中: 1 有一个特定的结点&#xff0c;称为该树的根(root)结点&#xff1b; 2 …

GPT-4o:突出优势 和 应用场景

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

centos官方yum源不可用 解决方案(随手记)

昨天用yum安装软件的时候&#xff0c;就报错了 [rootop01 ~]# yum install -y net-tools CentOS Stream 8 - AppStream 73 B/s | 38 B 00:00 Error: Failed to download metadata for repo appstream: Cannot prepare internal mirrorlis…

从零开始:疾控中心实验室装修攻略,让你的实验室一步到位!

在当今充满挑战和变化的世界中&#xff0c;疾病的控制和预防成为了人类生存与发展的重要课题。而疾控中心作为防控疾病的核心机构&#xff0c;其疾控中心实验室设计建设显得尤为重要。下面广州实验室装修公司小编将分享疾控中心实验室设计建设方案&#xff0c;为疾病防控工作提…

“冻干”凭什么好吃不肥喵?既能当零食又可做主食的冻干分享

近年来&#xff0c;冻干猫粮因其高品质而备受喜爱&#xff0c;吸引了无数猫主人的目光&#xff0c;像我这样的资深养猫人早已开始选择冻干喂养。但新手养猫的人&#xff0c;可能会感到迷茫&#xff1a;冻干猫粮到底是什么&#xff1f;冻干可以一直当主食喂吗&#xff1f; 一、…

TqdmWarning: IProgress not found. Please update jupyter and ipywidgets.

jupyter notebook报错 在pycharm的terminal中 安装完成后就不会再报错了

缓存方法返回值

1. 业务需求 前端用户查询数据时,数据查询缓慢耗费时间; 基于缓存中间件实现缓存方法返回值:实现流程用户第一次查询时在数据库查询,并将查询的返回值存储在缓存中间件中,在缓存有效期内前端用户再次查询时,从缓存中间件缓存获取 2. 基于Redis实现 参考1 2.1 简单实现 引入…

电源小白入门学习10——浪涌、防浪涌器件、浪涌保护芯片

浪涌、防浪涌器件、浪涌保护芯片 浪涌浪涌保护器件的分类与原理保险丝TVS二极管新防护电路 浪涌 浪涌&#xff0c;相信不少学习过电子的同学或多或少都通过这个词&#xff0c;但是到底什么是浪涌呢&#xff0c;GPT给我的答案是这样的&#xff1a; 浪涌&#xff0c;也称为瞬态…

MS21112S单通道 LVDS 差分线路接收器

MS21112S 是一款单通道低压差分信号 (LVDS) 线 路接收器。在输入共模电压范围内&#xff0c;差分接收器可以 将 100mV 的差分输入电压转换成有效的逻辑输出。 该芯片可应用于 100Ω 的受控阻抗介质上&#xff0c;进行点对 点基带数据传输。传输介质可以是印刷电路板、…

分水岭算法分割和霍夫变换识别图像中的硬币

首先解释一下第一种分水岭算法&#xff1a; 一、分水岭算法 分水岭算法是一种基于拓扑学的图像分割技术&#xff0c;广泛应用于图像处理和计算机视觉领域。它将图像视为一个拓扑表面&#xff0c;其中亮度值代表高度。算法的目标是通过模拟雨水从山顶流到山谷的过程&#xff0…

数据库存储过程和锁机制

存储过程 存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的&#xff0c;存储过程思想上很简单,就是数据库SQL语言层面的代码封装与有重用 …

【动态规划-BM69 把数字翻译成字符串】

题目 BM69 把数字翻译成字符串 描述 有一种将字母编码成数字的方式&#xff1a;‘a’->1, ‘b->2’, … , ‘z->26’。 现在给一串数字&#xff0c;返回有多少种可能的译码结果 分析 特判一个‘0’的情况 后面可以用动态规划&#xff1a; dp[n]为考虑前n个字符时&…

VMware虚拟机与MobaXterm建立远程连接失败

VMware虚拟机与MobaXterm建立远程连接失败 首先可以检查一下是不是虚拟机的ssh服务并不存在 解决方法&#xff1a; 1.更新镜像源 yum -y update 这个过程会有点久&#xff0c;请耐心等待 2.安装ssh yum install openssh-server 3.启动ssh systemctl restart sshd 4.查…

计算机毕业设计基于YOLOv8的头盔检测系统

1、安装Anaconda 官网下载或者哔哩哔哩有的up分享 https://www.anaconda.com/download 版本无所谓&#xff0c;安装位置不要有中文就行 2、创建环境yolov8 winR打开命令行 conda create -n yolov8 python3.9 3、打开源码 下载下来放到你想放的目录&#xff0c;直接用pyCharm或者…

NSSCTF CRYPTO MISC题解(一)

陇剑杯 2021刷题记录_[陇剑杯 2021]签到-CSDN博客 [陇剑杯 2021]签到 下载附件压缩包&#xff0c;解压后得到 后缀为.pcpang&#xff0c;为流量包&#xff0c;流量分析&#xff0c;使用wireshark打开 {NSSCTF} [陇剑杯 2021]签到 详解-CSDN博客 选择统计里面的协议分级 发现流…

go语言接口之接口值

概念上讲一个接口的值&#xff0c;接口值&#xff0c;由两个部分组成&#xff0c;一个具体的类型和那个类型的值。它们 被称为接口的动态类型和动态值。对于像Go语言这种静态类型的语言&#xff0c;类型是编译期的概 念&#xff1b;因此一个类型不是一个值。在我们的概念模型中…

DexCap——斯坦福李飞飞团队泡茶机器人:更好数据收集系统的原理解析、源码剖析

前言 2023年7月&#xff0c;我司组建大模型项目开发团队&#xff0c;从最开始的论文审稿&#xff0c;演变成目前的两大赋能方向 大模型应用方面&#xff0c;以微调和RAG为代表 除了论文审稿微调之外&#xff0c;目前我司内部正在逐一开发论文翻译、论文对话、论文idea提炼、论…