• GD32F10 串口通信


    1. 什么是通信

    通信,指人与人或人与自然之间通过某种行为或媒介进行的信息交流与传递,从广义上指需要信息的双方或多方在不违背各自意愿的情况下采用任意方法,任意媒质,将信息从某方准确安全地传送到另方。通信双方如果想正确传输数据就需要约定不违背的协议。

    通信在不同的环境下有不同的解释,在出现电波传递通信后通信(Communication)被单一解释为信息的传递,是指由一地向另一地进行信息的传输与交换,其目的是传输消息。

    然而,通信是在人类实践过程中随着社会生产力的发展对传递消息的要求不断提升使得人类文明不断进步。

    在各种各样的通信方式中,利用“电”来传递消息的通信方法称为电信(Telecommunication),这种通信具有迅速、准确、可靠等特点,且几乎不受时间、地点、空间、距离的限制,因而得到了飞速发展和广泛应用;

    在现今因电波的快捷性使得从远古人类物质交换过程中就结合文化交流与实体经济不断积累进步的实物性通信(邮政通信)被人类理解为制约经济发展的阻碍。

    在古代,人类通过驿站、飞鸽传书、烽火报警、符号、身体语言、眼神、触碰等方式进行信息传递。

    在现代科学水平的飞速发展,相继出现了无线电、固定电话、移动电话、互联网甚至视频电话等各种通信方式。通信技术拉近了人与人之间的距离,提高了经济的效率,深刻地改变了人类的生活方式和社会面貌。

    1. 分类

    1. 按传输媒质分类

    有线通信:是指传输媒质为导线、电缆、光缆、波导、纳米材料等形式的通信,其特点是媒质能看得见,摸得着(明线通信、电缆通信、光缆通信、光纤光缆通信)。

    无线通信:是指传输媒质看不见、摸不着(如电磁波)的一种通信形式 [2]  。

    (微波通信、短波通信、移动通信、卫星通信、 散射通信)。

    2. 按信道中传输的信号分类

    模拟信号:凡信号的某一参量(如连续波的振幅、频率、相位,脉冲波的振幅、宽度、位置等)可以取无限多个数值,且直接与消息相对应的,模拟信号有时也称连续信号。 这个连续是指信号的某一参量可以连续变化。

    数字信号:凡信号的某一参量只能取有限个数值,并且常常不直接与消息相对应的,也称离散信号 [1]  。

    3. 按工作频段分类

    长波通信。

    中波通信。

    短波通信。

    微波通信。

    4. 按调制方式分类

    基带传输:是指信号没有经过调制而直接送到信道中去传输

    的通信方式。

    频带传输:是指信号经过调制后再送到信道中传输,接收端

    有相应解调措施的通信方式。

    5.按通信双方的分工及数据传输方向分类。

    对于点对点之间的通信,按消息传送的方向,通信方式可分为单工通信、半双工通信及全双工通信三种。

    所谓单工通信,是指消息只能单方向进行传输的一种通信工作方式。单工通信的例子很多,如广播、遥控、无线寻呼等。这里,信号(消息)只从广播发射台、遥控器和无线寻呼中心分别传到收音机、遥控对象和BP 机上。

    所谓半双工通信方式,是指通信双方都能收发消息,但不能同时进行收和发的工作方式。对讲机、收发报机等都是这种通信方式。

    所谓全双工通信,是指通信双方可同时进行双向传输消息的工作方式。在这种方式下,双方都可同时进行收发消息。很明显,全双工通信的信道必须是双向信道。生活中全双工通信的例子非常多,如普通电话、手机等。

    2. 组成

    实现信息传递所需的一切技术设备和传输媒质的合称为通信系统。

    信源:消息的产生地,其作用是把各种消息转换成原始电信号,称之为消息信号或基带信号。电话机、电视摄像机和电传机、计算机等各种数字终端设备就是信源。

    发送设备:将信源和信道匹配起来,即将信源产生的消息信号变换为适合在信道中搬移的场合,调制是最常见的变换方式。对需要频谱搬移场合,调制是最常见的变换方式。对数字通信系统来说,发送设备常常又分为信源编码与信道编码。

    信道:传输信号的物理媒质。

    噪声源:是通信系统中各种设备以及信道中所固有的,为了分析方便,把噪声源视为各处噪声的集中表现而抽象加入到信道。

    接收设备:完成发送设备的反变换,即进行解调、译码、解码等等。它的任务是从带有干扰的接收信号中正确恢复出相应的原始基带信号来。

    信宿:传输信息的归宿点,其作用是将复原的原始信号转换成相应的信息

     2. 通信的传输方式。

    现在普遍双工比较多。 

    3. 通信中的并行,串行

    现在普遍串行比较多。 

     4. 通信中的同步,异步。

     

    5. (USART)异步通信 的数据帧格式。

    1.接收方怎么知道你什么时候发送。当发送完怎么知道你发送结束。所以在发送一个字节时会先发送一个起始位。在空闲的时候因为是高电平。当发送起始位是0,当接收方收到0就知道你要发送数据了就准备好接收数据。当发完一个字节时。这时又会发送一个停止位1。当接收方接收到停止位1就知道你发送完了。发送一个字节需要发送10个位。起始位(一位)+ 一个字节(八位) + 起始位(一位)。如图:奇偶校验位可以先不考虑。
     

    2. 具体发送方多久发送一个bit呢?

    从而引出波特率。如果波特率是9600。那么1/9600s传输1bit。

    1s传输9600bit。但对于接收方以多大的速度去接收呢。如果速度慢会丢bit。一般采样频率是

    传输频率的16倍。这样才能保证稳定不丢。

    如果单片机是发送方。笔记本电脑是接收方。我们不需要考虑接收问题。笔记本电脑硬件会自己处理。但是显示接收的上位机软件要配置跟下位机一样波特率,停止位,数据位,是否有校验位。

    如果单片机是接收方。笔记本电脑是发送方。需要考虑采样频率的问题。

    3. 具体波特率怎么产生呢?

    在库函数里设置波特率参数。就可以产生。有相应的硬件电路。我们不需要管。如果像有些51单片机是没有波特率产生电路的。需要用一个定时器配置来产生。

    对于异步通信字节与字节传输是可以间隔传输的。接收方接收到起始就知道有数据来。接收到停止位知道一个字节接收完毕。等一下字节也是同样方式。

     

    6. USART的框图

    对于收发就看圈的部分即可

     7. dome (串口发送数据)

    使用串口0 RX:PA9 TX:PA10

    串口初始化步骤:(这个dome没用中断)

     

    usart_comm.h

    1. #ifndef USART_COMM_H
    2. #define USART_COMM_H
    3. /**
    4. @brief: 串口处理的模块
    5. */
    6. #include "gd32f10x.h"
    7. void usart0_init(uint32_t baudval);
    8. void usart0_send_byte(uint8_t ch);
    9. void usart0_send_string(uint8_t *ch);
    10. #endif

    usart_cmm.c

    1. #include "usart_comm.h"
    2. /**
    3. *@brief: usart0的初始化
    4. *@param baudval: 波特率
    5. *@retval NONE
    6. */
    7. void usart0_init(uint32_t baudval){
    8. /* 初始化时钟和对应的io口 */
    9. rcu_periph_clock_enable(RCU_GPIOA);
    10. rcu_periph_clock_enable(RCU_USART0);
    11. gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* TX 推挽复用 */
    12. gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* RX 浮空输入 */
    13. // 配置usart0的工作参数
    14. usart_deinit(USART0); /* 重置初始化 */
    15. usart_baudrate_set(USART0, baudval); //波特率
    16. usart_parity_config(USART0, USART_PM_NONE); //无奇偶校验
    17. usart_word_length_set(USART0, USART_WL_8BIT); //bit长度
    18. usart_stop_bit_set(USART0, USART_STB_1BIT); //停止位
    19. usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); //使能发送
    20. usart_receive_config(USART0, USART_RECEIVE_ENABLE); //使能接收
    21. usart_enable(USART0); /* 使能串口 */
    22. }
    23. /**
    24. *@brief: usart0发送一个字节
    25. *@param ch:待发送字节
    26. *@retval NONE
    27. */
    28. void usart0_send_byte(uint8_t ch){
    29. usart_data_transmit(USART0, ch);
    30. while(usart_flag_get(USART0, USART_FLAG_TBE)==RESET); /* 等待发送完成 */
    31. }
    32. /**
    33. *@brief: usart0发送字符串
    34. *@param ch:待发送字符串指针
    35. *@retval NONE
    36. */
    37. void usart0_send_string(uint8_t *ch){
    38. uint32_t k = 0;
    39. while(*(ch+k) != '\0'){
    40. usart0_send_byte(*(ch+k));
    41. k++;
    42. }
    43. }

    main.c

    1. #include "systick.h"
    2. #include "usart_comm.h"
    3. int main(){
    4. systick_config();
    5. usart0_init(9600);
    6. while(1){
    7. usart0_send_string((uint8_t *)"hello UART.\n"); //自动会填充字符串结束符'\0'
    8. delay_1ms(1000); //等待1s
    9. }
    10. }

    结果:

    8.  dome (串口发送接收数据中断)

    通过上位机发指令来控制下位机的LED,继电器的操作。

    继电器硬件图:

    J1接到PB15. 控制PB15来控制继电器是否工作。

    LED硬件图:

    具体上下位机的协议规定:

    一帧有5个字节:帧头(0xFF),设备ID,设备号,设备指令,帧尾(0x00).

     

     relay_ctrl.h(继电器)

    1. #ifndef RELAY_CTRL_H
    2. #define RELAY_CTRL_H
    3. #include "gd32f10x.h"
    4. //定义继电器序号的宏
    5. #define RELAY_1 0x01
    6. void relay_init(void);
    7. void relay_connect(uint8_t relay_num);
    8. void relay_disconnect(uint8_t relay_num);
    9. #endif

     relay_ctrl.c(继电器)

    1. #include "relay_ctrl.h"
    2. // 继电器控制io口的初始化
    3. void relay_init(void){
    4. rcu_periph_clock_enable(RCU_GPIOB);
    5. gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
    6. //默认让继电器断开
    7. relay_disconnect(RELAY_1);
    8. }
    9. // 控制继电器吸合
    10. void relay_connect(uint8_t relay_num){
    11. switch(relay_num){
    12. case RELAY_1:
    13. gpio_bit_set(GPIOB, GPIO_PIN_15);
    14. break;
    15. default:
    16. break;
    17. }
    18. }
    19. // 控制继电器断开
    20. void relay_disconnect(uint8_t relay_num){
    21. switch(relay_num){
    22. case RELAY_1:
    23. gpio_bit_reset(GPIOB, GPIO_PIN_15);
    24. break;
    25. default:
    26. break;
    27. }
    28. }

    led_ctrl.h(LED)

    1. #ifndef LED_CTRL_H
    2. #define LED_CTRL_H
    3. #include "gd32f10x.h"
    4. //LED序号的宏定义
    5. #define LED_1 0x01
    6. #define LED_2 0x02
    7. void led_init(void);
    8. void led_open(uint8_t led_num); //led点亮
    9. void led_close(uint8_t led_num); //led熄灭
    10. #endif

     led_ctrl.c(LED)

    1. #include "led_ctrl.h"
    2. void led_init(void){
    3. rcu_periph_clock_enable(RCU_GPIOB);
    4. gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0|GPIO_PIN_1);
    5. led_close(LED_1);
    6. led_close(LED_2);
    7. }
    8. //led点亮
    9. void led_open(uint8_t led_num){
    10. switch(led_num){
    11. case LED_1:
    12. gpio_bit_set(GPIOB, GPIO_PIN_0);
    13. break;
    14. case LED_2:
    15. gpio_bit_set(GPIOB, GPIO_PIN_1);
    16. break;
    17. default:
    18. break;
    19. }
    20. }
    21. //led熄灭
    22. void led_close(uint8_t led_num){
    23. switch(led_num){
    24. case LED_1:
    25. gpio_bit_reset(GPIOB, GPIO_PIN_0);
    26. break;
    27. case LED_2:
    28. gpio_bit_reset(GPIOB, GPIO_PIN_1);
    29. break;
    30. default:
    31. break;
    32. }
    33. }

     usart_comm.h(串口)

    1. #ifndef USART_COMM_H
    2. #define USART_COMM_H
    3. /**
    4. @brief: 串口处理的模块
    5. */
    6. #include "gd32f10x.h"
    7. // 定义数据帧的宏
    8. #define HEX_HEAD 0xFF
    9. #define HEX_TAIL 0x00
    10. #define LED_ID 0x01
    11. #define RELAY_ID 0x02
    12. void usart0_init(uint32_t baudval);
    13. void usart0_send_byte(uint8_t ch);
    14. void usart0_send_string(uint8_t *ch);
    15. void deal_rec_buff(void); // 串口通信协议hex数据帧缓存处理函数
    16. #endif

     usart_comm.c(串口)

    1. #include "usart_comm.h"
    2. #include "led_ctrl.h"
    3. #include "relay_ctrl.h"
    4. uint8_t recv_buff[5]; /* 保存结束的数据 */
    5. uint8_t recv_buff_len = 0;
    6. /**
    7. *@brief: usart0的初始化
    8. *@param baudval: 波特率
    9. *@retval NONE
    10. */
    11. void usart0_init(uint32_t baudval){
    12. /* 初始化时钟和对应的io口 */
    13. rcu_periph_clock_enable(RCU_GPIOA);
    14. rcu_periph_clock_enable(RCU_USART0);
    15. gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* TX 推挽复用 */
    16. gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* RX 浮空输入 */
    17. // 配置usart0的工作参数
    18. usart_deinit(USART0);
    19. usart_baudrate_set(USART0, baudval); //波特率
    20. usart_parity_config(USART0, USART_PM_NONE); //无奇偶校验
    21. usart_word_length_set(USART0, USART_WL_8BIT); //bit长度
    22. usart_stop_bit_set(USART0, USART_STB_1BIT); //停止位
    23. usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); //使能发送
    24. usart_receive_config(USART0, USART_RECEIVE_ENABLE); //使能接收
    25. usart_enable(USART0); /* 使能串口 */
    26. // 使能usart0的中断
    27. usart_interrupt_enable(USART0, USART_INT_RBNE);
    28. /* 中断优先级 */
    29. nvic_irq_enable(USART0_IRQn, 2U, 2U);
    30. usart0_send_string((uint8_t *)"usart0 opened succeed.");
    31. }
    32. /**
    33. *@brief: usart0发送一个字节
    34. *@param ch:待发送字节
    35. *@retval NONE
    36. */
    37. void usart0_send_byte(uint8_t ch){
    38. usart_data_transmit(USART0, ch);
    39. while(usart_flag_get(USART0, USART_FLAG_TBE)==RESET); /* 等待发送完成 */
    40. }
    41. /**
    42. *@brief: usart0发送字符串
    43. *@param ch:待发送字符串指针
    44. *@retval NONE
    45. */
    46. void usart0_send_string(uint8_t *ch){
    47. uint32_t k = 0;
    48. while(*(ch+k) != '\0'){
    49. usart0_send_byte(*(ch+k));
    50. k++;
    51. }
    52. }
    53. // USART0的中断响应函数
    54. void USART0_IRQHandler(void){
    55. uint8_t temp;
    56. if(SET == usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)){
    57. usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
    58. temp = usart_data_receive(USART0);
    59. if(temp == HEX_HEAD){ //数据帧帧头
    60. recv_buff_len = 0;
    61. recv_buff[recv_buff_len] = temp;
    62. recv_buff_len++;
    63. }else if(temp == HEX_TAIL){ //数据帧帧尾
    64. if(recv_buff_len == 4){
    65. recv_buff[recv_buff_len] = temp;
    66. deal_rec_buff();
    67. }else{
    68. usart0_send_string((uint8_t *)"error");
    69. }
    70. }else{
    71. if(recv_buff_len > 0 && recv_buff_len < 4){
    72. recv_buff[recv_buff_len] = temp;
    73. recv_buff_len++;
    74. }else{
    75. usart0_send_string((uint8_t *)"error");
    76. }
    77. }
    78. }
    79. }
    80. // 串口通信协议hex数据帧缓存处理函数
    81. void deal_rec_buff(void){
    82. switch(recv_buff[1]){
    83. case LED_ID: //LED的控制
    84. if(recv_buff[3] == 0x01){
    85. led_open(recv_buff[2]);
    86. }else if(recv_buff[3] == 0x02){
    87. led_close(recv_buff[2]);
    88. }
    89. break;
    90. case RELAY_ID: //继电器的控制
    91. if(recv_buff[3] == 0x01){
    92. relay_connect(recv_buff[2]);
    93. }else if(recv_buff[3] == 0x02){
    94. relay_disconnect(recv_buff[2]);
    95. }
    96. break;
    97. default: //出错
    98. usart0_send_string((uint8_t *)"error");
    99. break;
    100. }
    101. }

     main.c

    1. #include "systick.h"
    2. #include "usart_comm.h"
    3. #include "led_ctrl.h"
    4. #include "relay_ctrl.h"
    5. int main(){
    6. systick_config();
    7. usart0_init(9600); /* 波特率:9600 */
    8. led_init();
    9. relay_init();
    10. while(1){
    11. }
    12. }

  • 相关阅读:
    React+Typescript+react-router 6 创建路由操作
    Android技术分享| 视频通话开发流程(一)
    【slam十四讲第二版】【课本例题代码向】【第十讲~后端2】
    用Rust开发一个类似SQL Server的数据库系统的步骤和关键技术
    Fabric v2.5区块链应用开发实战大纲
    Springboot @GetMapping 自动接收对象参数源码分析
    C++:程序在mian函数执行前后做了哪些工作?
    vue 第三方组件按需引入,最后项目的包体积真的变小了吗?
    防止员工私自拷贝公司资料
    多线程案例(单例、阻塞队列、生消模型、定时器)
  • 原文地址:https://blog.csdn.net/qq_41328470/article/details/133513263