zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    201521123042 《Java程序设计》 第10周学习总结
    201521123042《Java程序设计》 第9周学习总结
    201521123042 《java程序设计》 第八周学习总结
    201521123042《Java程序设计》 第7周学习总结
    201521123042 《Java程序设计》第6周学习总结
    201521123042 《Java程序设计》第5周学习总结
    markdown 模板
    201521123042 《Java程序设计》第4周学习总结
    201521123042 《Java程序设计》第3周学习总结
    201521123038 《Java程序设计》 第十二周学习总结
  • 原文地址:https://www.cnblogs.com/laoxiongzhijia/p/14429852.html
Copyright © 2011-2022 走看看