IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 串口通信——基于STM32MP157A -> 正文阅读

[嵌入式]串口通信——基于STM32MP157A

本实验将利用UART总线,采用串口通信的方式,发送字符串

一:串口通信方式

1:直连方式

? ? ? ? 串口共有三根线:RXD(接收数据线)、TXD(发送数据线)、GND(接地线)

? ? ? ? UART总线连接的是soc与target,串口与串口相连时,RXD与对方的TXD相连,GND与GND相连。

?

?2:USB转串口的连接方式

????????SOC采用的是TTL电平,TTL高电平为+5V,低电平为0V;串口电平为RS232电平,高电平为+15v~+3v,低电平为-15v~-3v

?3:ST-LINK仿真器

? ? ? ? ST-LINK仿真器的作用,既是完成串口和usb口的转换。在ST-LINK仿真器内部有一个芯片(STM32F103),这个芯片,完成USB口和串口之间转换(在STM32F103内部固化一段程序,这段程序不开源,这段程序可以USB口和串口之间转换)。

?4:通信协议

串口通信的协议如图所示

?此为一个完整的数据帧。

在电脑的串口工具内配置串口信息时,设置位112500波特率,8N1。

8N1的意思为:8位数据位,N(无奇偶校验位),1位停止位

如下图所示?

?协议解析

? ? ? ? 空闲态:UART总线不在传输数据的时候,总线处于空闲状态,为高电平

? ? ? ? 起始信号:通信开始的标志

? ? ? ? 数据位:位数由自己指定,注意,数据先发送高位,再发送低位(简单的来说,就是要反着填入数据)

? ? ? ? 奇偶校验位:校验数据是否正确(个人感觉没啥用)

? ? ? ? ? ? ? ? ? ? ? ? 奇校验:数据位的1和校验位的1加起来数量为奇数

? ? ? ? ? ? ? ? ? ? ? ? 偶校验:数据位的1和校验位的1加起来数量为偶数

? ? ? ? 停止信号:数据发送完毕以后,回到高电平,校准时钟信号

校验时钟信号的必要性

? ? ? ? 因为串口通信为异步通信,通信双方的时钟源相互独立。虽然一开始设置了双方时钟源保持一致,但是在实际发送数据的过程中,每发送一帧数据,都会产生误差,越往后,误差会越累积越大,最终变得难以忽视。为了避免这种情况的发生,每发送一帧数据,都需要进行一次时钟信号校准。

二:分析电路图

先找到UART总线的TX端口和RX端口?

?再去主板上寻找对应的引脚

?

?所以

UART4_RX------>PB2
UART4_TX------>PG11

显然,这俩是GPIOB与GPIOG寄存器管的

三:分析芯片手册

由之前的原理图和电路图可知,我们要用到两类寄存器,GPIO和UART,具体为GPIOB、GPIOG、UART4

1:RCC章节分析

老样子,先在芯片手册里找总线,GPIO前两个实验找过了,在AHB4总线

?UART在APB1总线

?我们本次编写程序依旧采用现成的库,就不去在意起始地址和地址偏移量了,如果想自己封装地址结构体,可以参考我的点亮LED灯——基于STM32MP157A_老K殿下的博客-CSDN博客(引流.jpg)

即为:
RCC_MP_AHB4ENSETR[1] = 1
RCC_MP_AHB4ENSETR[6] = 1

RCC_MP_APB1ENSETR

即为
RCC_MP_APB1ENSETR[16] = 1

2:GPIO章节分析

GPIOx_MODER

设置PB2和PG11引脚为复用功能
    GPIOB_MODER[5:4] = 10 ------->设置PB2引脚为复用功能
    GPIOG_MODER[23:22] = 10 ------->设置PG11引脚为复用功能

GPIOx_AFRx寄存器

有AFRL和AFRH两个寄存器,两个引脚分别位于AFRL和AFRH上

首先分析AFRL上的

?然后到《stm32mp157a》芯片手册上查看

?故,选择AF8模式,即1000

设置PB2引脚为复用功能UART4_Rx
    GPIOB_AFRL[11:8] = 1000 ------->设置PB2引脚为复用功能UART4_Rx

?然后是PG11引脚,它在AFRH寄存器上

??

设置PG11引脚为复用功能UART4_Tx
    GPIOG_AFRH[15:12] = 0110  ------->设置PG11引脚为复用功能UART4_Tx

3:UART章节

通过以上分析可知,设置寄存器:
1.USART_CR1:设置数据位宽度,以及将相应位进行使能
2.USART_CR2:设置停止位
3.USART_BRR:设置波特率---->设置的采样率有关
4.USART_RDR :设置接收数据寄存器
5.USART_TDR :设置发送数据寄存器
6.USART_ISR:设置状态寄存器
7.USART_PRESC :设置时钟分频器

?USART_CR1寄存器

?

?

?

?

?

?

需要设置如下位:
USART_CR1[28][12] = 00 ------->设置串口8位数据位
USART_CR1[15] = 0 ------->设置串口16倍采样率,会影响波特率的计算
USART_CR1[10] = 0------->设置串口无奇偶校验位
USART_CR1[3] = 1------->设置串口发送寄存器使能
USART_CR1[2] = 1------->设置串口接收寄存器使能
USART_CR1[0] = 1------->设置串口接收使能

USART_CR2寄存器

?

 USART_CR2[13:12] = 00 ------->设置串口1位停止位

?USART_BRR寄存器

设置波特率

?波特率的计算方法为:

需要参考芯片手册53.5.7章节,进行计算,波特率寄存器的设置,与采样率有关
设置串口波特率为115200bps,系统提供的串口时钟源:64MHZ
BRR = 64MHZ / 115200 = 0x22b
USART4_BRR = 0x22b ------->设置串口波特率为115200

USART_RDR寄存器

RDR寄存器是存放接收数据的寄存器

?USART_TDR寄存器

TDR寄存器是存放要发送的数据的寄存器,数据存放在[8:0]位

?USART_PRESC寄存器

仅能在[3:0]位输入数据,设置不分频USART_ISR寄存器

?USART_ISR寄存器

??

?

?至此,全部寄存器分析完毕,接下来是代码实现

四:代码实现

因为寄存器部分代码已经分析过了,就不再浪费时间逐一填,直接附上初始化UART4的功能函数源代码

寄存器初始化

// 初始化的函数
void uart4_init()
{
    // 1. 使能GPIOB,GPIOG外设的时钟 RCC_MP_AHB4ENSETR[1][6] = 0b1
    RCC->MP_AHB4ENSETR = (0x1 << 1) | (0x1 << 6);
    // 2. 设置PB2, 和 PG11引脚为复用的功能
            // GPIOB_MODER[5:4] = 0b10  GPIOG_MODER[23:22] = 0b10
    GPIOB->MODER &= (~(0x3 << 4));
    GPIOB->MODER |= (0x2 << 4);
    GPIOG->MODER &= (~(0x3 << 22));
    GPIOG->MODER |= (0x2 << 22);
 
    // 3. 设置PB2引脚为UART4_RX功能  GPIOB_AFRL[11:8] --> AF8 --> 0b1000
        // 设置PG11引脚为UART4_TX功能  GPIOG_AFRH[15:12] --> AF6 --> 0b0110
    GPIOB->AFRL &= (~(0xF << 8));
    GPIOB->AFRL |= (0x8 << 8);
    GPIOG->AFRH &= (~(0xF << 12));
    GPIOG->AFRH |= (0x6 << 12);
 
    // 4. 使能UART4外设的时钟  RCC_MP_APB1ENSETR[16] = 0b1
    RCC->MP_APB1ENSETR = (0x1 << 16);
 
    // 5. 判断UART4串口是否使能,如果使能则禁止串口
    if (USART4->CR1 & (0x1 << 0)) {
        delay_ms(2000);  // 等待之前的串口的数据发送完成之后在禁止串口
        USART4->CR1 &= (~(0x1 << 0));  // 禁止串口的使能
    }
    // 6. 设置数据位为8位的数据宽度 USART4_CR1[28][12] = 0b00
    USART4->CR1 &= ~((0x1 << 12) | (0x1 << 28));
    // 7. 禁止校验位,不使用校验  USART4_CR1[10] = 0b0
    USART4->CR1 &= (~(0x1 << 10));
    // 8. 设置串口的采样率为16倍或者8倍,最终会影响波特率的计算  USART4_CR1[15]
    USART4->CR1 &= (~(0x1 << 15));
    // 9. 设置停止位的个数为1位  USART4_CR2[13:12] = 0b00
    USART4->CR2 &= (~(0x3 << 12));
    // 10. 设置串口时钟的分频寄存器 USART4_PRERC[3:0]  最终也会影响波特率的计算
            // usart_ker_ck 时钟源的频率等于 64MHz
            // usart_ker_ck_pesc = usart_ker_ck / USART4_PRESC[3:0]
     USART4->PRESC &= (~(0xF << 0));
     // 11. 设置串口的波特率为115200bps  USART4_BRR[15:0]                    
     USART4->BRR = 0x22B;                                                    
     // 12. 使能串口发送器  USART4_CR1[3] = 0x1                              
     USART4->CR1 |= (0x1 << 3);                                              
     // 13. 使能串口接收器  USART4_CR1[2] = 0x1                              
     USART4->CR1 |= (0x1 << 2);                                              
     // 14. 使能串口        USART4_CR1[0] = 0x1                              
     USART4->CR1 |= (0x1 << 0);                                              
 }                                            

?头文件用别人写好的、现成的库

#include "stm32mp1xx_uart.h"
#include "stm32mp1xx_rcc.h"
#include "stm32mp1xx_gpio.h"
#define LEN  50

现在的关键,在于字符串发送接收函数的实现

我们要根据串口的通信协议,手动模拟串口数据包(如果谁忘了通信协议是什么样的,自行往上翻阅,写在开头)

要发送字符串,先要实现单个字符的收发

发送单个字符

void put_char(const char str)
{
    //1.判断发送数据寄存器是否有数据 ISR[7]
    //读0:发送数据寄存器满,需要等待
    //读1:发送数据寄存器为空,才可以发送下一个字节数据
    while(!(USART4->ISR & (0x1 << 7)));

    //2.将要发送的字符,写入到发送数据寄存器中
    USART4->TDR = str;

    //3.判断发送数据是否发送完成
    //读0:发送数据没有完成,需要等待
    //读1:发送数据完成,可以发送下一帧数据
    while(!(USART4->ISR & (0x1 << 6)));
}

接收一个字符

char get_char()
{
    char ch;
    //1.判断接收寄存器是否有数据可读 ISR[5]
    //读0:没有数据可读,需要等待
    //读1:有数据可读
    while(!(USART4->ISR & (0x1 << 5)));

    //2.将接收数据寄存器中的内容读出来
    ch = USART4->RDR;
    return ch;
}

发送字符串

发送字符串有些许不同,要手都补上换行的功能,在pc中“回车”是相当于'\n'与'\r'的组合,\n是换行,\r是回到行首,所以,在写代码时,要手动补上这两个字符

//3.发送一个字符串
void put_string(const char* str)
{
    //判断是否为'\0'
    //一个一个字符的进行发送
    while(*str)
    {
        put_char(*str++);
    }
    put_char('\n');
    put_char('\r');
}

?接收字符串

收到的字符串,存在数组中

char buffer[50] = {0};

char* get_string()
{
    unsigned int i;
    //1.循环进行接收
    //2.循环实现:接收一个字符之后,发送一个字符
    //当键盘回车建按下之后,代表字符串接收结束'\r'
    for(i=0;i<49;i++)
    {
        buffer[i] = get_char();
        put_char(buffer[i]);
        if(buffer[i] == '\r')
            break;
    }
    //3.字符串补'\0'
    buffer[i] = '\0';
    put_char('\n');
    return buffer;
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:58:04  更:2022-10-08 20:59:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 2:10:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计