ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

串口の二三事

2022-07-20 20:02:06  阅读:264  来源: 互联网

标签:UART huart1 rx unsigned 二三 char user 串口


串口の二三事

这两天去夏令营,又捣鼓了一下好久没搞过的单片机。在用串口的时候,感觉熟悉又陌生,故今天整理一下串口在开发中的一些常见疑问,以供日后查阅。

串口收发的原理

详情见通俗易懂的UART协议帧格式 - 知乎 (zhihu.com)

UART帧格式,也称UART协议,每一位的时长与波特率有关:

UART帧格式,也称UART协议,其内容如下:

  • 起始位:发送1位逻辑0(低电平),开始传输数据。
  • 数据位:可以是5~8位的数据,先发低位,再发高位,一般常见的就是8位(1个字节),其他的如7位的ASCII码。
  • 校验位:奇偶校验,将数据位加上校验位,1的位数为偶数(偶校验),1的位数4为奇数(奇校验)。
  • 停止位:停止位是数据传输结束的标志,可以是1/1.5/2位的逻辑1(高电平)。
  • 空闲位:空闲时数据线为高电平状态,代表无数据传输。

这里解答了一个疑问:串口是怎么知道自己收到了数据呢?答案就是通过判断有无收到满足协议的电平变化来判断。

UART与USART

  • UART:通用异步收发器(Universal Asynchronous Receiver/Transmitter);

  • USART:通用同步/异步串行收发器(Universal Synchronous/Asynchronous Receiver/Transmitter)。若使用异步方式,那将和UART无区别,如果是同步,则需要多一根时钟线(USART_CK).

数据类型转换

HAL函数要求的传递参数是unsigned char *uint_8 * ,故在实际开发中经常需要处理数据类型转化的问题。

unsigned char <=> char

  • 在内存中都占据8位(1字节);
  • unsigned char范围是0~255,char范围是-128~127;
  • 英文字符的ASCII码小于127,故均能表示一个英文字符
// char -> unsigned char
char a[32];
b = (unsigned char *) a; // 只要a中不包含负数都没问题

// unsigned char -> char
unsigned char a[32];
b = (char *) a; // 只要a[i]≤127都没问题

unsigned char <=> 数字

  • short = 16bit, int = 32bit, long = 64bit;
  • float = 32bit, double = 64bit;
// 数字 -> unsigned char* (会有warning)
# include <stdio.h>
sprintf(unsigned char *, "%d %u %o %x %f...", int, unsigned int, int_8, int_16, float);

// unsigned char* -> 数字
# include <stdlib.h>
unsigned char string[32];

int tmp = atoi(string);

double tmp = atof(string);
double tmp = strtod(string, (char **)NULL);

long tmp = strtol(string, (char **)NULL, intbase); // intbase为进制

其中,strtod的用法可参考C 库函数 – strtod() | 菜鸟教程 (runoob.com).

*浮点数的存储方式

查阅过程中顺便看了下float类型的存储方式,详见https://blog.csdn.net/wuing2/article/details/90143635

以 8.25 为例进行分析:
8:二进制位1000
0.25:为2^-2,即为0.01
因此,8.25转换成二进制位1000.01
用科学记数法表示为:2^3*(1.00001)
因此:
符号位:0(表示正数)
指数位:3+127=130=100000010
尾数:00001-000000-000000-000000
注意,因为用科学记数法,所以最前面肯定是1,故不记录了。
因此,整体数据为 0-10000010-00001-000000-000000-000000

接收不定长数据

以下所用函数的简介,请见STM32 非阻塞HAL_UART_Receive_IT解析与实际应用 - 知乎 (zhihu.com)

在实际开发中,经常遇到需要接收不定长字符串的问题,这里参考hal库串口接收,整理两种常用方法。

方法一 通过特定结束符判断

通信双方约定,用特定的字符作为结束,比如把0xff作为结束符,则收到0xff就把数据截断。对于ASCII码,正常情况下是不会发送0x0D与0x0A(回车与换行)的,所以可以用作结束符。

结构体定义

// usart.h
#define RX_LENGTH       1
#define RX_LENGTH_MAX   64

typedef struct {
  unsigned char rx_buf_[RX_LENGTH_MAX];
  unsigned char rx_flag_;
  unsigned int rx_count_;
  unsigned char rx_temp_[RX_LENGTH];
} UART_UserHandleTypeDef;

// usart.c
UART_UserHandleTypeDef user_huart1 = {
    .rx_buf_ = {0},
    .rx_count_ = 0,
    .rx_flag_ = 0,
    .rx_temp_ = 0
};

重载回调函数

// usart.c
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  if (huart->Instance == USART1) {
    user_huart1.rx_buf_[user_huart1.rx_count_] = user_huart1.rx_temp_[0];
    user_huart1.rx_count_++;
    if (0x0a == user_huart1.rx_temp_[0]) { // special character -  "0x0a"
      user_huart1.rx_flag_ = 1;
    }
    HAL_UART_Receive_IT(&huart1, (uint8_t *) user_huart1.rx_temp_, RX_LENGTH);
  }
}

主循环判断

// main.c
HAL_UART_Receive_IT(&huart1, (uint8_t *) user_huart1.rx_temp_, RX_LENGTH);

while (1) {
	if (user_huart1.rx_flag_) {
		HAL_UART_Transmit(&huart1, user_huart1.rx_buf_, user_huart1.rx_count_, 0x10);    //发送接收到的数据
        for (int i = 0; i < user_huart1.rx_count_; i++)
            user_huart1.rx_buf_[i] = 0;
        user_huart1.rx_count_ = 0;
        user_huart1.rx_flag_ = 0;
    }

方法二 通过超时判断

如果串口在一定的时间内没有收到新的数据,可以认为一组数据已经接收完毕了。

结构体定义

// usart.h
#define RX_LENGTH       1
#define RX_LENGTH_MAX   64

typedef struct {
  unsigned char rx_buf_[RX_LENGTH_MAX];
  unsigned char rx_flag_;
  unsigned int rx_count_;
  unsigned char rx_temp_[RX_LENGTH];
} UART_UserHandleTypeDef;

// usart.c
UART_UserHandleTypeDef user_huart1 = {
    .rx_buf_ = {0},
    .rx_count_ = 0,
    .rx_flag_ = 0,
    .rx_temp_ = 0
};

重载回调函数

// usart.c
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  if (huart->Instance == USART1) {
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    if (0 == user_huart1.rx_count_) {
      __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
      HAL_TIM_Base_Start_IT(&htim3);
    }
    user_huart1.rx_buf_[user_huart1.rx_count_] = user_huart1.rx_temp_[0];
    user_huart1.rx_count_++;
    HAL_UART_Receive_IT(&huart1, (uint8_t *) user_huart1.rx_temp_, RX_LENGTH);
  }
}

// tim.c
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim == (&htim3)) {
    user_huart1.rx_flag_ = 1;
    HAL_TIM_Base_Stop_IT(&htim3); // 关闭定时
  }
}

主循环判断

// main.c
HAL_UART_Receive_IT(&huart1, (uint8_t *) user_huart1.rx_temp_, RX_LENGTH);

while (1) {
	if (user_huart1.rx_flag_) {
		HAL_UART_Transmit(&huart1, user_huart1.rx_buf_, user_huart1.rx_count_, 0x10);    //发送接收到的数据
        for (int i = 0; i < user_huart1.rx_count_; i++)
            user_huart1.rx_buf_[i] = 0;
        user_huart1.rx_count_ = 0;
        user_huart1.rx_flag_ = 0;
    }

标签:UART,huart1,rx,unsigned,二三,char,user,串口
来源: https://www.cnblogs.com/c-pidan/p/16499613.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有