20%初学者容易搞混的串口基础知识整理

2019-07-21 08:15发布

本帖最后由 秋天 于 2018-10-22 14:42 编辑

在做项目的时候是不是发现串口是最常用的通信接口,所以我自己整理了一下关于串口的一些常识性知识,方便自己学习。
          关键词: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个UART3个USART。在日常我们就可以说,它有5个串口,因为USART可以配置成UART
    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,蓝牙等等,这些模块都是通过串口来控制。所以作为一个工程师,必须要搞懂串口。首先我们了解一下我们的工具,大家可以到我的淘宝店里看看:https://shop296209296.taobao.com/index.htm?spm=a1z10.5-c.w5002-18352947751.2.acd4173daNW4Pn
想要的可以联系我下面我们通过Loto虚拟示波器采集不同波特率的TTL的UART 波形:

对比理论的时序图,我们找找起始位和停止位,这个数据是多少?有时候我们面试会考一个题目,那就是UART波特率9600的时候,它的一个bit是多长时间,这个怎么算?初学者肯定蒙了,我用串口直接就配置然后发数据就行了,从来没算过,下面我总结一下:
波特率2400:1/2400416.67*10^(-6)s=416.67us波特率4800:1/4800208.33*10^(-6)s=208.33us波特率9600:1/9600104.17*10^(-6)s=104.17us波特率19200:1/1920052.08*10^(-6)s=52.08us波特率57600:1/5760017.36*10^(-6)s=17.36us波特率115200:1/1152008.68*10^(-6)s=8.68us
对比我们用Loto虚拟示波器采集的数据看看是不是一致呢?

我们算这个数据有什么用呢?是的!没错!就是模拟串口!当单片机串口不够用的时候项目中会用到。接下来我会分析几个论友写的代码,逐步深入理解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不稳定)[mw_shl_code=c,true]#define BuadRate_9600        100
u8 len = 0;        //接收计数
u8 USART_buf[11];  //接收缓冲区

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[t]);
        }
}
        
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[len++] = recvData;        
                        return;
                }
                if(OI_RXD)
                {
                        recvData |= (1 << (recvStat - 1));
                }else{
                        recvData &= ~(1 << (recvStat - 1));
                }        
  }               
}
[/mw_shl_code]

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
20条回答
秋天
1楼-- · 2019-07-21 08:47
 精彩回答 2  元偷偷看……
HXYDJ
2楼-- · 2019-07-21 13:55
讲的非常详细,感谢分享。
秋天
3楼-- · 2019-07-21 19:30
HXYDJ 发表于 2018-10-22 15:56
讲的非常详细,感谢分享。

帮忙去店铺踩踩,我会更加努力的
agelarui
4楼-- · 2019-07-21 20:56
这示波器必须的顶
秋天
5楼-- · 2019-07-22 00:53
agelarui 发表于 2018-10-23 10:38
这示波器必须的顶

那我也标记一下您,谢谢支持
秋天
6楼-- · 2019-07-22 05:12
 精彩回答 2  元偷偷看……

一周热门 更多>