STM8学习笔记---普通IO口模拟串口功能

2020-06-07来源: eefocus关键字:STM8  普通IO口  模拟串口

串口在产品应用中很常见,但是单片机的默认带的串口往往比较少,有时候就会出现串口不够用,所以就想着能不能用普通IO口模拟串口来实现串口的功能。


要模拟串口首先要清楚串口数据传输过程中的原理。

在这里插入图片描述

常用的串口格式为 1位起始位,8位数据位,无校验位,1位结束位。起始位为低电平,结束位为高电平。数据0为低电平,数据1为高电平。


所以最简单的串口传输一个字节总共有10个电平变化,每个电平的宽度由波特率决定的。


具体的串口数据分析,可以参考这篇文章:STM8学习笔记---通过示波器分析串口数据。


下面看一个通过波特率如何计算每个位的电平宽度。


发送一个字节,以stm8中9600bit/s的波特率计算的过程为例(1秒钟传输9600位)。

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


通过计算可以看出来,如果波特率为9600时,一个位的电平宽度要为104us。

在这里插入图片描述

上图中就是一个字节的完整波形,起始位为低电平,结束位为高电平,中间8位为数据位,无校验位。数据位是低位在前,高位在后。也就是和起始位挨着的是最低位,和结束位挨着的是最高位。通过这个波形就可以分析出,发送的数据是0x00。


下面在看一看0X01的波形。

在这里插入图片描述

起始位为低电平,结束位为高电平,中间数据位有一个高电平,其余都是低电平,按照低位在前,高位在后规律,数据位就是0000 0001 刚好是16进制的0x01。


知道了串口数据的格式,下面就开始写代码,先写串口发送代码。


//输出1

void Send_1( void )

{

    SIM_TXD = 1;

}

//输出0

void Send_0( void )

{

    SIM_TXD = 0;

}

//发送一个字节

void WriteByte( unsigned char sdata ) //波特率9600

{

   //如果数据误码率比较高,可以修改delay_us延时时间

    unsigned char i;

    unsigned char value = 0;

    //发送起始位

    Send_0();

    delay_us( 100 );

    //发送数据位

    for( i = 0; i < 8; i++ )

    {

        value = ( sdata & 0x01 ); //先传低位

        if( value )

        {

            Send_1();

        }

        else

        {

            Send_0();

        }

        delay_us( 100 );        //经测试延时100us 数据没有误差 示波器上观察波形时间为104us左右

        sdata = sdata >> 1;

    }

    //停止位

    Send_1();

    delay_us( 100 );           

}

       首先发送起始位,将IO口电平拉低,延时104us,下来发送8位数据位,低位在前,高位在后,每发送一位就延时104us。最后发送结束位,将IO口电平置高,在延时104us。这样一个字节就发送结束了。


      由于延时程序使用软件实现的,所以延时精度不是很高,延时参数设置为100时,通过示波器看一位刚好是104us。由于不同的编译器,不同的单片机,通过软件计算的延时时间不同,所以这里的延时时间最好通过示波器来测,延时时间的精确度决定了通信的误码率。延时时间精确通信误码率就低,延时误差比较大,通信误码率就高。


下面再看接收程序


//接收一个字节

void ReadByte( void )

{

    unsigned char i, value = 0;

    if( !SIM_RXD )    //RXD_IN RXD等于0时开始接收

    {

        //等过起始位 起始位为低电平

        delay_us( 100 );

        //接收8位数据位

        for( i = 0; i < 8; i++ )

        {

            value >>= 1;

            if( SIM_RXD )      //RXD_IN

            {

                value |= 0x80;

            }

            delay_us( 100 );

        }

        //等过结束位 结束位为高电平

        //delay_us(100);  //一次性接收大量数据时,防止程序执行代码时浪费时间,在结束符时可以不用等待

        RecFlag = 1;      //标记已经接收到了数据

        RecBuf = value;

        return;

    }

}

        接收数据就更好理解了,读取IO口的电平,如果出现低电平就开始接收数据,然后读取8个数据位的电平,在等待结束位结束。这样一个字节的数据就接收完成了。


    那么如何判断什么时候去接收串口的数据呢?


   有两种方式去实现,一种是在死循环中用查询方式去判断,一直读取IO的的电平,如果出现低电平就认为串口有数据发送进来了。


实现代码如下:


//查询方式接收数据

void ReadString( void )

{

    unsigned int cnt = 0, i = 0, j = 0;

    unsigned char recstr[100] = {0};

    _Bool send_F = 0;

    while( 1 )

    {

        if( !RecFlag )            //未收到数据时扫描

        {

            ReadByte();          //扫描数据

            cnt++;              //统计扫描次数

        }

        else if( RecFlag )        //收到数据后读取数据 如果接收到数据没有读取时 不会继续接收数据

        {

            cnt = 0;

            RecFlag = 0;

            recstr[i++] = RecBuf; //存储接收的数据

            RecBuf = 0;

            send_F = 1;            //标记数据可以发送

        }

        if( ( cnt >= 100 ) && ( send_F == 1 ) ) //扫描次数超过100 并且可以发送数据

        {

            cnt = 0;

            WriteString( recstr );  //发送接收到的数据

            send_F = 0;             //清除发送标志

            i = 0;                  //清除接收数据数组下标

            for( j = 0; j < 100; j++ ) //清除接收数据缓冲区

            {

                recstr[j] = 0;

            }

            return;                 //发送完数据后返回

        }

        else if( cnt > 500 )        //没有接收到数据 超时退出

        {

            cnt = 0;

            return;

        }

    }

}

这种方式实现起来比较简单,但是对于程序编写比较麻烦,因为要一直监视者IO口,所以程序干其他事情时,很有可能错过数据的接收。可以用第二种方式,IO口中断来判断什么时候要开始接收数据,将IO口设置为下降沿中断,当有下降沿出现时,说明串口有数据进来了,然后再去读取串口数据。没有中断发生时,程序就可以干其他事情了。


实现代码如下:


//通道PC3口的下降沿中断检测数据

//PC3口中断 RXD

#pragma vector = 7                  // IAR中的中断号,要在STVD中的中断号上加2

__interrupt void RXDInterrupt( void )

{

    PC_CR2 &= ~( 1 << 3 );      //禁止外部中断

    ReadByte();

    if( recEnd == 0x01 )

    {

        if( RecBuf == 0x0a )      //收到结束符 0x0a 标记数据接收完毕

        {

            recEnd |= 0x02;

            recCNT = 0;

        }

    }

    if( recEnd != 0x03 )

    {

        if( RecBuf != 0x0d )        //结束符为回车换行符 0x0d 0x0a

        {

            recBUFF[recCNT++] = RecBuf; //没收到结束符存储数据

            RecBuf = 0;

        }

        else if( RecBuf == 0x0d )   //收到0x0d 标记结束符开始

        {

            recEnd |= 0x01;

        }

    }

    PC_CR2 |= ( 1 << 3 );       //使能外部中断

}

当出现下降沿之后进入中断程序,这时候要关闭外部中断,开始读取IO口电平状态。若不关闭中断,在读取IO电平的过程中中断还会不停的进入,这样就会影响读取数据的准确性。所以进入中断会首先要关闭中断

[1] [2] [3]
关键字:STM8  普通IO口  模拟串口 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic499330.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:IAR FOR STM8 同一个工程芯片选择003F3可以编译003K3提示空间不足
下一篇:单片机对19264液晶屏的驱动

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM8 复位寄存器地址映射
复位状态寄存器(RST_SR)请参考对应的数据手册了解基地址信息。(表7:STM8复位寄存器地址映射)
发表于 2020-07-25
<font color='red'>STM8</font> 复位寄存器地址映射
STM8 复位(RST)寄存器
复位状态寄存器(RST_SR)地址偏移值:0x00复位值:未定义位7:5保留位,必须保持为0位4EMCF:EMC复位标志由硬件置位,可通过软件写"1"清除0:无EMC复位发生;1:有一个EMC复位发生(可能的复位原因:互补寄存器或选项字节不匹配)。位3SWIMF:SWIM复位标志位由硬件置位,可通过软件写"1"清除0:无SWIM复位发生;1:有一个SWIM复位发生。位2ILLOPF:非法操作码复位标志位由硬件置位,可通过软件写"1"清除0:无非法操作码复位发生;1:有一个非法操作码复位发生。位1IWDGF:独立型看门狗复位标志位由硬件置位,可通过软件写"
发表于 2020-07-25
<font color='red'>STM8</font> 复位(R<font color='red'>ST</font>)寄存器
STM8 存储器保护
读保护当选项字节中的ROP字节被编程为'0xAA'时,读保护就生效了。这种情况下,无论写保护是否生效,在ICP模式中(使用SWIM接口)读取或修改FLASH程序存储器和DATA区域都是被禁止的。即使认为没有什么保护是完全不可破解的,对于一个通用微处理器来说,STM8的读保护的特性也提供了一个非常高水平的保护级别。可以在ICP模式中通过对选项字节中的ROP字节重新编程来解除程序存储器、UBC和DATA区域的读保护。在这种情况下,程序存储器、UBC、DATA区域以及选项字节都被自动擦除,器件也可以被重新编程了。存储器存取安全系统(MASS)STM8在复位以后,主程序和DATA区域都被自动保护以防止无意的写操作。在试图
发表于 2020-07-25
STM8 用户启动区域(UBC)
用户启动区域(UBC)包含有复位和中断向量表,它可用于存储IAP及通讯程序。UBC有一个两级保护结构可保护用户代码及数据在IAP编程中免于无意的擦除或修改。这意味着该区域总是写保护的,而且写保护不能通过使用MASS密钥来解锁。在ICP模式下(使用SWIM接口)可以通过修改选项字节来配置UBC的大小。UBC选项字节指定了分配在UBC中的页的数量。UBC区域的起始地址是0x00 8000。 可以通过读取UBC选项字节来获得UBC区域的大小。请参考图6,图7和图8来了解UBC区域的存储器映射。对于选项字节部分,请参考相应的数据手册了解更多的UBC选项字节的细节。(图六:小容量STM8S的UBC区域大小)1. UBC[7:0]=0x00
发表于 2020-07-25
<font color='red'>STM8</font> 用户启动区域(UBC)
STM8 存储器组织结构
STM8S的EEPROM以32位字长(每字4字节)为基础组织起来。根据不同的器件,存储器组织机 构有所不同:小容量STM8S器件8K FLASH程序存储器,每页 64字节,共 128页640字节数据 EEPROM,每页 64字节,共 10页。数据 EEPROM包括一页的选项字节(64字节)。中容量STM8S器件从 16K到 32K FLASH程序存储器,每页 512字节,最多 64页1K字节数据EEPROM,每页 512字节,共 2页。数据 EEPROM包括一页的选项字节(512字节)。大容量STM8S器件从 64K到 128K FLASH程序存储器,每页 512字节,最多 256页从 1K到 2K字节数据 EEPROM,每页
发表于 2020-07-25
<font color='red'>STM8</font> 存储器组织结构
STM8 FLASH主要特性
STM8内部的FLASH程序存储器和数据EEPROM由一组通用寄存器来控制。用户可以使用这些寄存器来编程或擦除存储器的内容、设置写保护、或者配置特定的低功耗模式。用户也可以对器件的选项字节(Option byte)进行编程。STM8S EEPROM分为两个存储器阵列:最多至 128K字节的FLASH程序存储器,不同的器件容量有所不同。请参考4.4存储器组织结构了解更多细节。最多至 2K字节的数据EEPROM(包括option byte-选择字节),不同的器件容量有所不同。请参考4.4存储器组织结构了解更多细节。编程模式字节编程和自动快速字节编程(没有擦除操作)字编程块编程和快速块编程(没有擦除操作)在编程/擦除操作结束时和发生非法
发表于 2020-07-25
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved