STM32技术--USATR通信

以下以STM32F407ZGT6为例

串口

1.通信:设备间信息的交互

 有线通信:以太网,串口,USB,CAN等

  无线通信:wifi,蓝牙,红外,2/3/4/5G,广播,NB-IOT等

2.通信的分类

  并行通信:一次传输多位数据,传输速度快,多使用在近距离传输,CPU中的总线,MCU与内存,下载烧录器等

  

  串行通信:一次传输一位数据。传输距离远

串行通信按照数据传输方向:

  单工:数据单方向传输  -----广播,收音机

  半双工:数据可以双向传输,同一时刻只能一个方向传输(A到B)-----对讲机

  全双工:数据可以双向传输  ---手机,SPI等

同步通信:接收时钟与发送时钟严格同步,通常要有同步时钟

异步通信:字符与字符之间的传送是完全异步的,位与位之间的传送基本上是同步的

串行通信

 串行通信接口(通常指COM接口),是采用串行方式扩展接口。

1.物理层

  a.管脚

    TXD数据发送管脚,RXD数据接收管脚,GND信号地

  b.连接方式

    直接相连,距离近,常用于芯片与模块之间,模块与模块之间通信

    

2.RS232------负逻辑电平

  

 3.数据链路层   --------------位协议  ----------232 协议

        起始位                           数据位                         奇偶校验位                      停止位

位数             1          5~8          1            1~2

电平        0           1/0           1/0            1

起始位:低电平  ------  数据传输的开始

数据位:5~8位

奇偶校验位:用来判断传输的数据是否正确(奇偶校验是不准确的校验方式)

    奇校验:数据位中1的个数+奇偶校验位中1的个数为奇数个。

    偶校验:数据位中1的个数+奇偶校验位中1的个数为偶数个。

     以奇校验为例:

        发送数据 1010 0010

        发送   1010 0010  0

        接收   1010 0011 0   -------错误

             1010 0001 0        -------正确(错误)

  可以看出当传输的数据如果1变为0的个数和0变为1的个数凑巧都为奇数个或偶数个的时候,奇偶校验的结果是正确,可是实际数据却发生了错误,可见奇偶校验是不准确的。

停止位:高电平  --数据传输结束

位:CLK一个时钟周期

比如2位停止位就是2个时钟周期的高电平就产生停止接收数据的信号,而数据位8位就是8个时钟周期可以产生8bit的数据,即每一个时钟周期传送的数据被当作一个bit来解析。

常用的数据传输格式:1+8+0+1

波特率:1s传输多少数据位 例115200采用1+8+0+1的协议,即每10位有一个字节(8位 1byte = 8bit)的有效数据,就是一秒钟传输115200/10/1024 = 11.25kb的有效数据

而波特率是怎么计算的呢?

这里Fck是时钟频率

OVER8 = 0,OVER16 = 1

USARTDIV是我们要写入到BRR寄存器的值,

所以计算公式为Fck/brr/(16或8),这里16或8是通过过采样选择的

初始化串口

1、查看原理图确定引脚

注意:我们在途中看到的RXD,TXD是网络标号,可能会出错,但是引脚是不会出错的,比如图中RXD里面的功能写的是USART_TX,TXD写的功能是USART_RX,所以在使用时一定要根据引脚号来确定功能再进行配置。

2.看引脚是否有用作串口的功能

PA9,PA10,一个用作发送数据,一个用作接收数据,但是这两个引脚的通用功能都是作为IO口的,复用功能中才有串口的功能,所以在配置的时候要作为复用功能来使用。

3、配置串口

(1)开时钟,串口,GPIOA

(2)把复用功能映射到引脚上

  高位寄存器控制(8~15)8个管脚,低位寄存器控制(0~7)

查看引脚复用映射来判断应该填入寄存器的值

 使用串口功能,所以在AFRH9,10两部分写入AF7(0111)就将串口的功能复用到引脚上了。

注意:AFR寄存器配置的时候高低位是以数组来配置的,高位将GPIOA-AFR[]的下标写成1,低位写0.

(3)配置pa9,pa10的工作模式

  pa9作为TX(发送)口所以要复用推挽输出

  pa10作为RX(接受)配置为复用输入(这里只需配置为复用,无上下拉即可)

  (4) 配置串口 

  串口一般要配置工作模式,通信协议,和波特率,这些都可以根据自己的需要进行配置。

  

USARTx_CR1寄存器需要配置的东西比较多,可以根据自己的选择进行配置,CR2寄存器目前我学到的只有一个关于通信协议的配置

在这一般会选择1个停止位。

波特率的配置:

在这里我们需要将波特率的效数部分和整数部分分别计算出来,然后写到USARTx_BRR这个寄存器中去,整数部分写在4~15位,小数部分要写入的值是通过公式计算(计算方法见上面)出来的值的效数部分乘16再写道BRR寄存器的低4位即可。

最后通过CR1寄存器使能串口,使能发送,使能接收即可使用串口进行通信了。

(5)回显函数的思想

  用一个8位的变量接收从USARTx_DR(此时DR寄存器的值为通过串口接收的值)的值,再将变量的内容写入到USART_DR(如果向DR赋值,DR寄存器就是作为发送数据寄存器,如果取DR的值,则DR作为接收数据寄存器)中去,这样在串口助手中发送数据时就可以看到自己发送成功了没

(6)重定向printf函数

  printf作为文件流的标准输出流,是将数据发送到标准输出设备(即屏幕)如果我们想要通过printf函数把我们想要输出的内容打印到串口上,我们只要重写fputc()函数即可,具体操作是,因为所有数据在计算机中都是以二进制存储的,所以每个字符都有对应的值,我们只要将fputc中c这个形参赋给DR寄存器就可以将printf的内容重新发送到串口上了。

这是fputc函数的原型 int   fputc (int c, FILE *stream);,还有因为printf函数是在stdio.h这个头文件中的,但是我们的芯片不能放得下那么多内容,所以我们在使用时要勾选软件给我们提供的微库

 

这样就可以正常使用了,下面附上初始化代码。

 这是寄存器配置(STM32F407)

复制代码
#include "usart.h"
#include "stdio.h"
#include "string.h"
/*
函数名称    Usart1_Config
函数功能    初始化usart1
函数返回值 void
函数参数     u32 brr
*/
void Usart1_Config(u32 brr)
{
    float div=0;
    u32 div_m=0;//存放整数部分
    u32 div_f=0;//存放小数部分
    //开时钟 PA USART1
    RCC->AHB1ENR |= (1<<0);
    RCC->APB2ENR |= (1<<4);
    //配置工作模式 p9 p10 复用模式做 usart1
    GPIOA->AFR[1] &=~ (0xff<<4);
    GPIOA->AFR[1] |= (0x7<<4);
    GPIOA->AFR[1] |= (0x7<<8);
    //PA9 复用推挽输出
     GPIOA->MODER  &=~ (3<<18);
    GPIOA->MODER |= (2<<18);
    GPIOA->OTYPER &=~ (1<<9);
    GPIOA->OSPEEDR &=~ (3<<18);
    GPIOA->OSPEEDR |= (2<<18);
    GPIOA->PUPDR &=~ (3<<18);
    //pa10 
    GPIOA->MODER &=~ (3<<20);
    GPIOA->MODER |= (2<<20);
    GPIOA->PUPDR &=~ (3<<20);
    //配置串口
    USART1->CR1 &=~ (1<<12);//字长1+8+n
  USART1->CR1 &=~ (1<<10);    //禁止奇偶校验
    USART1->CR2 &=~ (3<<12); //停止位
    //接收发送使能
    USART1->CR1 |= (1<<2);
    USART1->CR1 |= (1<<3);
    //波特率
    div = 84000000.0/16/brr;
    div_m = (u32)div;
    div_f = (div-div_m)*16;
    USART1->BRR = (div_m<<4)|div_f;
    //使能串口
    USART1->CR1 |= (1<<13);
}

/*
函数名称    usart_Echo
函数功能    回显
函数返回值 无
函数参数     无
*/
void Usart1_Echo(void)
{
    u8 data=0;
    //接收数据
    //判断是否接收到数据
    while(!(USART1->SR & (1<<5)));
    //保存数据
    data = USART1->DR;
    //发送数据
    //判断上次数据是否发送完成
    while(!(USART1->SR & (1<<6)));
    USART1->DR = data;
}

u8 turn_led()
{
        u8 data;
        while(!(USART1->SR &(1<<5)));
        data = USART1->DR;
        while(!(USART1->SR & (1<<6)));
        USART1->DR = data;
        return data;
}


int fputc(int c, FILE * stream)
{
    //判断上次数据是否发送完成
    while(!(USART1->SR & (1<<6)));
    USART1->DR = c;
    return c;
}
复制代码

这是通过库函数配置(STM32F103)

复制代码
#include "usart.h"
#include "stdio.h"

void usart_Config(u32 brr)
{
    GPIO_InitTypeDef pa9;
    GPIO_InitTypeDef pa10;
    USART_InitTypeDef usart1;
    //pa9 tx pa10 rx usart1 开时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    
    //pa9 复用推挽输出
    pa9.GPIO_Mode = (GPIO_Mode_AF_PP);
    pa9.GPIO_Pin = (GPIO_Pin_9);
    pa9.GPIO_Speed = (GPIO_Speed_50MHz);
    GPIO_Init(GPIOA,&pa9);
    
    //pa10 浮空输入
    pa10.GPIO_Mode = (GPIO_Mode_IN_FLOATING);
    pa10.GPIO_Pin = (GPIO_Pin_10);
    GPIO_Init(GPIOA,&pa10);
    
    //配置usart1
    usart1.USART_BaudRate = brr;
    usart1.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    usart1.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    usart1.USART_Parity = USART_Parity_No;
    usart1.USART_StopBits = USART_StopBits_1;
    usart1.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1,&usart1);
    USART_Cmd(USART1,ENABLE);
    
    
}

void echo(void)
{
    u8 data;
    while((USART1->SR & (1<<5)) == 0);
    data = USART1->DR;
    while((USART1->SR & (1<<6)) == 0);
    USART1->DR = data;
}

int fputc(int c,FILE * stream)
{
    while((USART1->SR & (1<<6)) == 0);
    USART1->DR = c;
    return c;
}
原文地址:https://www.cnblogs.com/laoxiongzhijia/p/14429852.html