模拟串口--基于STM8普通IO口的模拟串口驱动程序

基于STM8普通IO口的模拟串口驱动程序

标准串口通讯数据的格式为:起始位(1) + 数据位(8) + 校验位(1) + 停止位(1)

串口通讯另外一个重要的的部分是设置波特率,波特率就是1秒钟内串口所传输的Bit(位)数。

关于采样频率:为了较小读取或者发送串行数据的误差,我们采取了在N(我用的是4次)次中断中,取固定位置的读取的数据。

我以stm8中9600波特率计算的过程为例:(1秒钟传输9600位)

可以计算出传输1位所需要的时间 T1 = 1/9600 约为104us

由此可知,发送一位数据,定时器中断的时间间隔应为 104/4 = 26us(4倍采样频率)

stm8 内部晶振频率为16M,我采用8分频也就是2M,故MCU震荡周期为 1/2M = 0.5us

由上面的计算我们可以知道要发送一位数据,定时器中断的初值应设为为 26/0.5 =52

以上为相关数据的计算过程,下面是模拟串口驱动程序和注释:

定时器中断与IO口配置:

void TIM3_Configuration(void)
{
  TIM3_DeInit();
  TIM3_TimeBaseInit(TIM3_PRESCALER_8,52);        //52  104
  TIM3_ITConfig(TIM3_IT_UPDATE ,ENABLE);
  TIM3_ARRPreloadConfig(ENABLE);
  TIM3_Cmd(ENABLE);                                                //DISABLE TIM3_Cmd(DISABLE)
}

//模拟串口引脚定义
#define SIM_UART_TX_PORT               GPIOC
#define SIM_UART_TX_PIN                GPIO_PIN_2
#define SimUartTxHigh()                (SIM_UART_TX_PORT->ODR |= (u8)(SIM_UART_TX_PIN))
#define SimUartTxLow()                 (SIM_UART_TX_PORT->ODR &= (u8)(~SIM_UART_TX_PIN)) 

#define SIM_UART_RX_PORT               GPIOC
#define SIM_UART_RX_PIN                GPIO_PIN_3
#define SimUartRxStatus()              (SIM_UART_RX_PORT->IDR & SIM_UART_RX_PIN)

GPIO_Init(SIM_UART_RX_PORT, SIM_UART_RX_PIN,GPIO_MODE_IN_PU_NO_IT);       

GPIO_Init(SIM_UART_TX_PORT, SIM_UART_TX_PIN,GPIO_MODE_OUT_PP_LOW_FAST);     
/* Includes ------------------------------------------------------------------*/
#include "stm8s.h"
#include "global.h"

//默认采样频率为4倍  一下为16M晶振 8分频 后计数器的装载值
//9600B 104us发送一位  4倍采样频率 故为26us发送一位
#define  SIM_BAUDRATE_9600     52
#define  SIM_BAUDRATE_4800     104
#define  SIM_BAUDRATE_2400     208

/* Private variables ---------------------------------------------------------*/
u8 RxByteIndex;        //接收字节索引
u8 RxSampFreq;         //采样频率控制 1/4
u8 TxXKCnt = 3;        //需要发送数据包的字节数
u8 SimUartRxBuff[10];  //接收数据包缓冲
u8 SimUartTxBuff[10] = {0x55, 0xaa, 0x66};
bool IsSimUartRxFinish;//是否接收完成标志
bool IsSimUartRecv;    //模拟串口是否处于接收状态

/* Private functions ---------------------------------------------------------*/
/* Public functions ----------------------------------------------------------*/
void <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">InterruptSimUart</span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;">(void);</span>
static void Drv_SimUartTxByte(void);
static void Drv_SimUartRxByte(void);

/*******************************************************************************
#Function      :   InterruptSimUart
#Description   :   模拟串口中断调用程序  切换发送与接收
#Parameter     :   NULL
#Return        :   NULL
#AuthorAndData :   huangzhigang 20141013
*******************************************************************************/
void InterruptSimUart(void)
{
   if(IsSimUartRxFinish)   //接收完成后立刻发送数据  也可以自己定义什么时候发送数据
   {
      IsSimUartRxFinish = FALSE;
      IsSimUartRecv = FALSE;
   }

   if(IsSimUartRecv)
   {
      Drv_SimUartRxByte();
   }
   else
   {
      Drv_SimUartTxByte();
   }
}

/*******************************************************************************
#Function      :   Drv_SimUartRxByte
#Description   :   模拟串口接收函数
#Parameter     :   NULL
#Return        :   NULL
#AuthorAndData :   huangzhigang 20141013
*******************************************************************************/
static void Drv_SimUartRxByte(void)
{
    static u8 RxBitNum;    //接收位计数
    static u8 Verify;      //校验码
    static u8 OverTime;    //接收超时计数
    static u8 s_u8Rxbuff;  //一字节接收缓存

    if(SimUartRxStatus())
    {
        OverTime++;
    }
    else
    {
         OverTime = 0;
    }

    if(OverTime > 44)
    {
        OverTime = 45;
        RxByteIndex = 0;
        RxBitNum = 0;
    }

    if((SimUartRxStatus()) && (RxBitNum == 0))
    {
        RxSampFreq = 0;
    }
    else
    {
       ++RxSampFreq;
    }

    if(RxSampFreq == 1)
    {
        if(RxBitNum == 0)                                           //低电平,起始位bit0
        {
            if(!SimUartRxStatus())
            {
                Verify = 0;
                s_u8Rxbuff = 0;
                RxBitNum++;
            }
        }
        else if((RxBitNum > 0) && (RxBitNum < 9))                   //数据位  bit1~8
        {
            if(SimUartRxStatus())                                   //高电平
            {
                s_u8Rxbuff = s_u8Rxbuff | (0x01 << (RxBitNum -1));
                Verify++;
            }

            RxBitNum++;
        }
        else if(RxBitNum == 9)                                      //校验位  bit9
        {
            RxBitNum++;

            if(Verify & 0x01)
            {
                if(SimUartRxStatus()) {RxBitNum = 0;}               //奇校验
            }
            else
            {
                if(!SimUartRxStatus()) {RxBitNum = 0;}
            }

        }
        else if(RxBitNum == 10)                                     //停止位 bit10
        {
            if(SimUartRxStatus())
            {
                RxBitNum = 0;

                if(RxByteIndex == 0)                                //头码1为0X55
                {
                    if(s_u8Rxbuff == 0x55)
                    {
                        SimUartRxBuff[RxByteIndex] = s_u8Rxbuff;
                        RxByteIndex++;
                    }
                    else
                    {
                        RxByteIndex = 0;
                    }

                    //TEST  测试  接收到一字节数据后马上回复
//                    IsSimUartRxFinish = TRUE;
                }
                else if(RxByteIndex == 1)                           //头码2为0Xaa
                {
                    if(s_u8Rxbuff == 0xaa)
                    {
                        SimUartRxBuff[RxByteIndex] = s_u8Rxbuff;
                        RxByteIndex++;
                    }
                    else
                    {
                        s_u8Rxbuff = 0;
                    }
                }
                else
                {
                      SimUartRxBuff[RxByteIndex] = s_u8Rxbuff;
                      RxByteIndex++;

                      if(RxByteIndex >= 2)                            //接收完一个数据包
                      {
                         //TODO: 20141013 hzg 验证校验码
                         RxByteIndex = 0;
                         IsSimUartRxFinish = TRUE;
                      }
                }
            }
        }
        else
        {
            RxBitNum = 0;
        }
    }
    else if(RxSampFreq > 3)
    {
        RxSampFreq = 0;
    } 

}

/*******************************************************************************
#Function      :   Drv_SimUartTxByte
#Description   :   模拟串口发送函数
#Parameter     :   NULL
#Return        :   NULL
#AuthorAndData :   huangzhigang 20141013
*******************************************************************************/
void Drv_SimUartTxByte(void)
{
    static bool SendFinish = TRUE;  //发送完成标志
    static u8 Verify;               //奇偶校验
    static u8 TxSampFreq = 0;       //发送计数  采样4次
    static u8 BitNum = 0;           //位计数
    static u8 ByteLock;             //发送字节锁定(防止在发送途中 发送数字被改变)
    static u8 TxIndex = 0;          //当前发送索引

    if(SendFinish)
    {
       SendFinish = FALSE;
       RxSampFreq = 0;
       BitNum = 0;

       if(TxIndex < TxXKCnt)        //控制发送的字节
       {
          ByteLock = SimUartTxBuff[TxIndex];
          TxIndex++;
          RxByteIndex = 0;
       }
       else
       {
          IsSimUartRecv = TRUE;
          SendFinish = TRUE;
          TIM3->ARRH = (uint8_t)(52 >> 8);   //定时器3计数器重装初值
          TIM3->ARRL = (uint8_t)(52);
          //            TxXKCnt = 0;
          TxIndex = 0;
       }
    }

    if(++TxSampFreq > 3)
    {
        if(BitNum == 0)                             //起始位
        {
            Verify = 0;
            SimUartTxLow();
            BitNum++;
        }
        else if((BitNum >0) && (BitNum < 9))        //数据位
        {
            if(0x01 & (ByteLock >> (BitNum-1)))     //先发低位
            {
                SimUartTxHigh();
                Verify++;
            }
            else
            {
                SimUartTxLow();
            }
            BitNum++;
        }
        else if(BitNum == 9)                        //校验位
        {
            if(0x01 & Verify)                       //奇校验
            {
                SimUartTxLow();                     //有奇数个1 则发送0
            }
            else
            {
                SimUartTxHigh();                    //有偶数个1则发送1
            }
            BitNum++;
        }
        else if(BitNum == 10)                       //停止位
        {
            SimUartTxHigh();
            SendFinish = TRUE;
            BitNum = 0;
        }

        TxSampFreq = 0;
    }
}
时间: 2024-10-09 15:39:07

模拟串口--基于STM8普通IO口的模拟串口驱动程序的相关文章

stm32 普通IO口模拟串口通信

普通IO口模拟串口通信 串口通信协议 串口传输 默认 波特率9600 1起始位 1停止位 其他0 数据位是8位(注意图上的给错了). 传输时,从起始位开始,从一个数据的低位(LSB)开始发送,如图从左向右的顺序,对电平拉高或拉低,最后停止位时拉高. 波特率大小,改变延时时间即可.例如9600 波特率    根据公式 : 1/9600=0.000104s(大致) 也就是说每发送1bit延时104us (下面我用9600波特率来说,代码用的是19200) 串口发送       将电平拉低 延时104

单片机小白学步系列(二十) IO口原理

IO口操作是单片机实践中最基本最重要的一个知识,本篇花了比较长的篇幅介绍IO口的原理.也是查阅了不少资料,确保内容正确无误,花了很长时间写的.IO口原理原本需要涉及很多深入的知识,而这里尽最大可能做了简化方便理解.这样对于以后解决各种IO口相关的问题会有很大的帮助. IO口等效模型是本人独创的方法,通过此模型,能有效的降低对IO口内部结构理解的难度.并且经查阅资料确认,这种模型和实际工作原理基本一致. ========================================== 前面说了

STM8S---独立按键IO口设置及按下事件问题

GPIO设置 按键检测 1 连续按键检测短按长按 关键代码 1 GPIO设置 ????????????????STM8 I/O 口引脚配置表 Px_DDR Px_CR1 Px_CR2 I/O 方式 引脚状态 0 0 0 输入 悬浮输入 0 0 1 输入 上拉输入 0 1 0 输入 中断悬浮输入 0 1 1 输入 中断上拉输入 1 0 0 输出 开漏输出 1 1 0 输出 推挽输出 1 x 1 输出 输出(最快速度为10MHZ) 对STM8S的IO配置,我们只需要操作五个寄存器就行了: 输出数据寄

STM32的IO口的8种配置

STM32的IO口的8种配置 1 STM32的输入输出管脚有下面8种可能的配置:(4输入+2输出+2复用输出) ① 浮空输入_IN_FLOATING ② 带上拉输入_IPU ③ 带下拉输入_IPD ④ 模拟输入_AIN ⑤ 开漏输出_OUT_OD ⑥ 推挽输出_OUT_PP ⑦ 复用功能的推挽输出_AF_PP ⑧ 复用功能的开漏输出_AF_OD 1.1 I/O口的输出模式下,有3种输出速度可选(2MHz.10MHz和50MHz),这个速度是指I/O口驱动电路的响应速度而不是输出信号的速度,输出信

STM32系列单片机IO口模式设置

STM32单片机的每组IO口都有4个32位配置寄存器用于配置GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR和GPIOx_PUPDR,2个32位数据寄存器用于配置输入和输出寄存器GPIOx_IDR和GPIOx_ODR,1个32位置位复位寄存器GPIOx_BSRR,1个32位锁定寄存器GPIOx_LCKR和2个32位复用功能选择寄存器GPIOx_AFRH和GPIOx_AFRL. GPIO的输出状态可以配置为推挽或开漏加上上拉或下拉.输出数据既可以来自输出数据寄存器,

STM32中IO口的8中工作模式

该文摘自:http://blog.csdn.net/kevinhg/article/details/17490273 一.推挽输出:可以输出高.低电平,连接数字器件:推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止.高低电平由IC的电源决定.         推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小.效率高.输出既可以向负载灌电流,也

单片机小白学步系列(二十二) IO口:蜂鸣器的使用/三极管的工作原理

2014-11-22更新:感谢Ireland同学的提醒,修改了PNP型三极管驱动电路,蜂鸣器从三极管的发射极改到了集电极,这样效果会更好,电路也更合理.同时增加了备注4. ====================================== 这一篇继续上一篇的内容,我们来做实验四:按键控制有源蜂鸣器,按下按键蜂鸣器响,释放按键不响. 蜂鸣器 实验四之前简单介绍下蜂鸣器.蜂鸣器有两种,无源蜂鸣器和有源蜂鸣器,一般用于发出报警的声音.声音是由震动产生的,大家都见过喇叭,喇叭里面有磁铁和线圈.

单片机小白学步系列(二十三) IO口原理知识补充:双向IO口、互补推挽、高阻态

由于之前考虑不周,本篇在IO口原理知识的基础上,进一步补充一些知识. ================================================= 双向IO口的输出:互补推挽 在51单片机的P0口工作在普通IO口模式下,为准双向IO口.而工作在第二功能状态下时,则为标准的双向IO口.由于双向IO口的输出,要求能输出高低电平,通常会采用互补推挽电路. 在第二功能状态下,51单片机P0口采用的是互补推挽的输出方式.何为互补推挽呢?下面是它的等效电路图. 当P0第二功能作为输出时

单片机小白学步系列(二十一) IO口:基本的LED和按键操作

本篇我们先介绍最基础的几个IO口操作.学完本篇,大家要能自己独立完成下面几个实验. 1.点亮LED 2.一个按键控制LED,按下灯亮,释放灯灭 3.两个按键控制一个LED,一个键开灯,一个键关灯 实验一 点亮LED的例子前面入门篇已经讲过了,程序写起来很简单,Keil编写程序并编译生成HEX文件,烧写进单片机的过程,也做了完整详细的介绍. 点亮LED利用了IO口输出操作.简单复习一下,最核心的两句代码是: sbit LED = P1^0; LED = 0; 第一句定义LED为sbit型变量,对应