串口的基本知识你掌握了吗 [复制链接]

2018年10月19日 15:49    发布者:wowo2018
在做项目的时候是不是发现串口是最常用的通信接口,所以我自己整理了一下关于串口的一些常识性知识,方便自己学习。
          关键词:Loto虚拟示波器,串口时序,电平标准,模拟串口
    我们常说的串口按电平分类的话有两种,一种是TTL电平的串口,一种是232电平的串口。首先我们先看一下这两种电平的区别。TTL电平标准:输出L:  <0.8V;  H: >2.4V,输入L:  <1.2V:   H: >2.0VTTL器件输出低电平要小于0.8V,高电平要大于2.4V,输入,低于1.2V就认为是0,高于2.0就认为是1.于是TTL电平的输入低电平的噪声容限就只有(O.8-0)/2=0.4V,高电平的噪声容限为(5-2.4)/2=1.3V.RS232标准:逻辑1的电平为-3~-15V,逻辑0的电平为+3~+15V,注意电平的定义反相了一次。
   然而TTL电平的串口还可以分为两种UART与USART。我们打开stm32cubeMX看到stm32F103有2个UART和3个USART。在日常我们就可以说,它有5个串口,因为USART可以配置成UART。http://forum.armfly.com/data/attachment/forum/201810/12/101643jiorm0w5w3wsswk5.png
http://forum.armfly.com/data/attachment/forum/201810/12/102320u9rmdbd9ibg59td1.png    UART与USART都是单片机上的串口通信,他们之间的区别如下:首先从名字上看:UART:universal asynchronous receiver and transmitter通用异步收/发器USART:universal synchronous asynchronous receiver and transmitter通用同步/异步收/发器  从名字上可以看出,USART在UART基础上增加了同步功能,即USART是UART的增强型,事实也确实是这样。但是具体增强到了什么地方呢?  其实当我们使用USART在异步通信的时候,它与UART没有什么区别,但是用在同步通信的时候,区别就很明显了:大家都知道同步通信需要时钟来触发数据传输,也就是说USART相对UART的区别之一就是能提供主动时钟。如STM32的USART可以提供时钟支持ISO7816的智能卡接口。

     在实际的项目中用的最多的还是UART ,配置为:N, 8 ,1.波特率常用的有2400,9600,57600,115200.我们用串口来干什么呢?用过一些模块的工程师会知道,很多模块都是通过串口来控制或者通过串口透传数据的,比如gsm(2g,4g),gps,wifi,蓝牙等等,这些模块都是通过串口来控制。所以作为一个工程师,必须要搞懂串口。下面我们通过Loto虚拟示波器采集不同波特率的UART 波形:http://forum.armfly.com/data/attachment/forum/201810/12/102801w029yrdbre2p286y.gifhttp://forum.armfly.com/data/attachment/forum/201810/12/102648ckbjnbjizog0dqll.gifhttp://forum.armfly.com/data/attachment/forum/201810/12/102627w45chbr7tr4z2040.gifhttp://forum.armfly.com/data/attachment/forum/201810/12/100755m6owzlbk6k8nbeko.gif
对比理论的时序图,我们找找起始位和停止位,这个数据是多少?http://forum.armfly.com/data/attachment/forum/201810/15/084245ilctwumcaxteme0j.png有时候我们面试会考一个题目,那就是UART波特率9600的时候,它的一个bit是多长时间,这个怎么算?初学者肯定蒙了,我用串口直接就配置然后发数据就行了,从来没算过,下面我总结一下:
波特率2400:1/2400≈416.67*10^(-6)s=416.67us波特率4800:1/4800≈208.33*10^(-6)s=208.33us波特率9600:1/9600≈104.17*10^(-6)s=104.17us波特率19200:1/19200≈52.08*10^(-6)s=52.08us波特率57600:1/57600≈17.36*10^(-6)s=17.36us波特率115200:1/115200≈8.68*10^(-6)s=8.68us
对比我们用Loto虚拟示波器采集的数据看看是不是一致呢?
http://forum.armfly.com/data/attachment/forum/201810/12/112534r9rn9wr8ruw28n7u.pnghttp://forum.armfly.com/data/attachment/forum/201810/12/112615lf27ss0p4432w6pp.pnghttp://forum.armfly.com/data/attachment/forum/201810/12/112633qdnjjd288jmt8dd8.pnghttp://forum.armfly.com/data/attachment/forum/201810/12/112643ujq2q7bpoqgqe55e.png
我们算这个数据有什么用呢?是的!没错!就是模拟串口!当单片机串口不够用的时候项目中会用到。接下来我会分析几个论友写的代码,逐步深入理解TTL串口。
下面这个模拟串口的代码:首先说它的发送吧,发送一个字节,先发送起始位OI_TXD = 0;  然后delay_us(BuadRate_9600);然后依次从低位开始检测Data的每一位,从而拉高拉低OI_TXD,然后延时,移位,重复8次,最后发送停止位发送完一个Data。接收的话:它是stm32开了个定时器4和一个外部中断.外部中断采到下降沿,然后定时器设置为107us中断一次,去采集一个bit,为什么是107us呢?因为此时波特率是9600,一个bit是104.17us,然后可以依次采集每个bit,直到采完一个data。(个人感觉107us不稳定)

[*]
[*]/**
[*]*软件串口的实现(IO模拟串口)
[*]* 波特率:9600    1-8-N
[*]* TXD : PC13
[*]* RXD : PB14
[*]* 使用外部中断对RXD的下降沿进行触发,使用定时器4按照9600波特率进行定时数据接收。
[*]* Demo功能: 接收11个数据,然后把接收到的数据发送出去
[*]*/
[*]
[*]
[*]#define OI_TXD        PCout(13)
[*]#define OI_RXD        PBin(14)
[*]
[*]#define BuadRate_9600        100
[*]
[*]u8 len = 0;        //接收计数
[*]u8 USART_buf;  //接收缓冲区
[*]
[*]enum{
[*]        COM_START_BIT,
[*]        COM_D0_BIT,
[*]        COM_D1_BIT,
[*]        COM_D2_BIT,
[*]        COM_D3_BIT,
[*]        COM_D4_BIT,
[*]        COM_D5_BIT,
[*]        COM_D6_BIT,
[*]        COM_D7_BIT,
[*]        COM_STOP_BIT,
[*]};
[*]
[*]u8 recvStat = COM_STOP_BIT;
[*]u8 recvData = 0;
[*]
[*]void IO_TXD(u8 Data)
[*]{
[*]        u8 i = 0;
[*]        OI_TXD = 0;
[*]        delay_us(BuadRate_9600);
[*]        for(i = 0; i < 8; i++)
[*]        {
[*]                if(Data&0x01)
[*]                        OI_TXD = 1;
[*]                else
[*]                        OI_TXD = 0;
[*]
[*]                delay_us(BuadRate_9600);
[*]                Data = Data>>1;
[*]        }
[*]        OI_TXD = 1;
[*]        delay_us(BuadRate_9600);
[*]}
[*]
[*]void USART_Send(u8 *buf, u8 len)
[*]{
[*]        u8 t;
[*]        for(t = 0; t < len; t++)
[*]        {
[*]                IO_TXD(buf);
[*]        }
[*]}
[*]
[*]void IOConfig(void)
[*]{
[*]        GPIO_InitTypeDef  GPIO_InitStructure;
[*]        NVIC_InitTypeDef NVIC_InitStructure;
[*]         EXTI_InitTypeDef EXTI_InitStruct;
[*]        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);         //使能PB,PC端口时钟
[*]
[*]         //SoftWare Serial TXD
[*]        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
[*]        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
[*]        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 //IO口速度为50MHz
[*]        GPIO_Init(GPIOC, &GPIO_InitStructure);
[*]        GPIO_SetBits(GPIOC,GPIO_Pin_13);
[*]
[*]
[*]        //SoftWare Serial RXD
[*]        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
[*]        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
[*]        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
[*]        GPIO_Init(GPIOB, &GPIO_InitStructure);
[*]
[*]        GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
[*]        EXTI_InitStruct.EXTI_Line = EXTI_Line14;
[*]        EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
[*]        EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断
[*]        EXTI_InitStruct.EXTI_LineCmd=ENABLE;
[*]        EXTI_Init(&EXTI_InitStruct);
[*]
[*]
[*]        NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ;
[*]        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
[*]        NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
[*]        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
[*]        NVIC_Init(&NVIC_InitStructure);
[*]
[*]}
[*]
[*]void TIM4_Int_Init(u16 arr,u16 psc)
[*]{
[*]        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
[*]        NVIC_InitTypeDef NVIC_InitStructure;
[*]
[*]        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
[*]
[*]        //定时器TIM4初始化
[*]        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
[*]        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
[*]        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
[*]        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
[*]        TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
[*]        TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
[*]        TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
[*]
[*]        //中断优先级NVIC设置
[*]        NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM4中断
[*]        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级1级
[*]        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级
[*]        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
[*]        NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
[*]}
[*]
[*]
[*]int main(void)
[*]{
[*]         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
[*]         delay_init();
[*]         IOConfig();
[*]         TIM4_Int_Init(107, 71);         //1M计数频率
[*]
[*]        while(1)
[*]        {
[*]                if(len > 10)
[*]                {
[*]                        len = 0;
[*]                        USART_Send(USART_buf,11);
[*]                }
[*]        }
[*]}
[*]
[*]void EXTI15_10_IRQHandler(void)
[*]{
[*]        if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)
[*]        {
[*]                if(OI_RXD == 0)
[*]                {
[*]                        if(recvStat == COM_STOP_BIT)
[*]                        {
[*]                                recvStat = COM_START_BIT;
[*]                                TIM_Cmd(TIM4, ENABLE);
[*]                        }
[*]                }
[*]                EXTI_ClearITPendingBit(EXTI_Line14);
[*]        }
[*]}
[*]
[*]void TIM4_IRQHandler(void)
[*]{
[*]        if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)
[*]        {
[*]                TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
[*]                 recvStat++;
[*]                if(recvStat == COM_STOP_BIT)
[*]                {
[*]                        TIM_Cmd(TIM4, DISABLE);
[*]                        USART_buf = recvData;
[*]                        return;
[*]                }
[*]                if(OI_RXD)
[*]                {
[*]                        recvData |= (1 << (recvStat - 1));
[*]                }else{
[*]                        recvData &= ~(1 << (recvStat - 1));
[*]                }
[*]  }
[*]}
[*]

复制代码
这里还有一个例子:
[*]#include "stm32f10x.h"
[*]#include "sys.h"
[*]#include "exti.h"
[*]#include "stdio.h"
[*]
[*]//加入以下代码,支持printf函数,而不需要选择use MicroLIB
[*]#if 1
[*]#pragma import(__use_no_semihosting)
[*]//标准库需要的支持函数
[*]struct __FILE
[*]{
[*]        int handle;
[*]
[*]};
[*]
[*]FILE __stdout;
[*]//定义_sys_exit()以避免使用半主机模式
[*]_sys_exit(int x)
[*]{
[*]        x = x;
[*]}
[*]//重定义fputc函数
[*]int fputc(int ch, FILE *f)
[*]{
[*]    void SendOneByte(u8 Byte);
[*]        SendOneByte((uint8_t) ch);
[*]        return ch;
[*]}
[*]#endif
[*]
[*]
[*]
[*]void SendOneByte(u8 Byte)
[*]{
[*]        u8 i=8,tmp;
[*]        TXD_low(); //发送起始位
[*]        delay_us(104);
[*]        //发送8位数据
[*]        for(i=0;i<8;i++)
[*]        {
[*]                tmp        = (Byte >> i) & 0x01;  //低位在前
[*]
[*]                if(tmp == 0)
[*]                {
[*]                        TXD_low();
[*]                        delay_us(104);        //0
[*]                }
[*]                else
[*]                {
[*]                        TXD_high();
[*]                        delay_us(104);        //1
[*]                }
[*]        }
[*]//        while(i--)
[*]//        {
[*]//          MNUSART_TXD = (Byte&0x01);     //先传低位
[*]//          delay_us(104);
[*]//          Byte = Byte>>1;
[*]//          //无校验位
[*]//          MNUSART_TXD=1;//发送结束位
[*]//          delay_us(104);
[*]
[*]//        }
[*]         TXD_high();
[*]         delay_us(104);
[*]
[*]}
[*]
[*]void SendBytes(u8 *str,u8 len)        //发送数组最好用这个,也可发送字符串
[*]{
[*]  u16 i;
[*]  for(i=0;i [*]  {
[*]           SendOneByte(str);
[*]
[*]  }
[*]
[*]}
[*]
[*]void SendStr(u8 *str) //发送字符串,发送数组如果带0x00就中断发送了
[*]{
[*]while(*str)
[*]SendOneByte(*str++);
[*]
[*]}
[*]
[*]int main(void)
[*]{
[*]  u8 len;
[*]  u8 hello[]={0x5a,0xa5,0x00,0x00,0x01};
[*]  delay_init();
[*]  EXTIX_Init();
[*]  //测试 发送一个字节
[*]  SendOneByte(0x00);
[*]  SendOneByte(0x01);
[*]  SendOneByte(0x02);
[*]//测试发送数组
[*]SendBytes(hello,5);
[*]//测试发送字符串
[*]// SendBytes("hello word",11);
[*]// SendStr("德玛西亚");
[*]
[*]  while(1)
[*]        {
[*]
[*]      if(USART_RX_STA&0x8000)
[*]          {
[*]             printf("\r\n您发送的消息为:\r\n\r\n");
[*]                   len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
[*]                 SendBytes(USART_RX_BUF,len);
[*]                 //也可以用下面的发送
[*]//                 SendStr(USART_RX_BUF);
[*]//                 for(len=0;len<200;len++)
[*]//                 USART_RX_BUF=0;
[*]                   printf("\r\n\r\n");//插入换行
[*]                USART_RX_STA=0;
[*]          }
[*]        }
[*]}
[*]
[*]

复制代码

[*]#include "exti.h"
[*]#include "stm32f10x_gpio.h"
[*]#include "stm32f10x_exti.h"
[*]#include "sys.h"
[*]
[*]
[*]u8 tmp;
[*]u8 USART_RX_BUF;
[*]u16 USART_RX_STA=0;
[*]//extern SendOneByte(u8 Byte);
[*]void EXTIX_Init(void)
[*]{
[*]                GPIO_InitTypeDef GPIO_InitStructure;
[*]                EXTI_InitTypeDef EXTI_InitStructure;
[*]                NVIC_InitTypeDef NVIC_InitStructure;
[*]                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
[*]
[*]                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                                 //PA.9 端口配置
[*]                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
[*]                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 //IO口速度为50MHz
[*]                GPIO_Init(GPIOA, &GPIO_InitStructure);                                         //根据设定参数初始化GPIOB.5
[*]                GPIO_SetBits(GPIOA,GPIO_Pin_9);
[*]
[*]                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
[*]                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                          //浮空输入
[*]                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
[*]                GPIO_Init(GPIOA, &GPIO_InitStructure);
[*]                GPIO_SetBits(GPIOA,GPIO_Pin_10);
[*]
[*]                //GPIOA.10 中断线以及中断初始化配置   下降沿触发
[*]                GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource10);
[*]
[*]                EXTI_InitStructure.EXTI_Line=EXTI_Line10;
[*]                EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
[*]                EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
[*]                EXTI_InitStructure.EXTI_LineCmd = ENABLE;
[*]                EXTI_Init(&EXTI_InitStructure);                 //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
[*]
[*]
[*]                NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
[*]                NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
[*]                NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
[*]                NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
[*]
[*]                NVIC_Init(&NVIC_InitStructure);
[*]
[*]
[*]}
[*]
[*]
[*]void EXTI15_10_IRQHandler(void)
[*]{
[*]   u8 i=9;
[*]   if(EXTI_GetITStatus(EXTI_Line10) != RESET)
[*]   {
[*]                /* Disable the Selected IRQ Channels -------------------------------------*/
[*]                NVIC->ICER =
[*]                (uint32_t)0x01 << (EXTI15_10_IRQn & (uint8_t)0x1F);
[*]                EXTI_ClearITPendingBit(EXTI_Line10);
[*]                delay_us(30);
[*]                while(i--)
[*]                {
[*]                tmp >>=1;
[*]                if(GPIOA->IDR&0x0400) tmp |=0x80;
[*]                delay_us(104);
[*]
[*]        }
[*]
[*]          if((USART_RX_STA&0x8000)==0)
[*]          {
[*]                        if(USART_RX_STA&0x4000)
[*]                        {
[*]                        if(tmp!=0x0a) USART_RX_STA=0;
[*]                        else USART_RX_STA |=0x8000;
[*]                        }
[*]                        else
[*]                        {
[*]                        if(tmp==0x0d) USART_RX_STA|=0x4000;
[*]                        else
[*]                        {
[*]                        USART_RX_BUF=tmp ;
[*]                        USART_RX_STA++;
[*]                        if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
[*]                        }
[*]                        }
[*]
[*]          }
[*]
[*]                EXTI_ClearITPendingBit(EXTI_Line10);
[*]                NVIC->ISER =
[*]                (uint32_t)0x01 << (EXTI15_10_IRQn & (uint8_t)0x1F);
[*]
[*]   }
[*]
[*]}