找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 928|回复: 6
打印 上一主题 下一主题
收起左侧

单片机IO引脚模拟串口程序

[复制链接]
跳转到指定楼层
楼主
ID:299910 发表于 2025-2-18 14:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#include"STC15W4K.H"
#define RECEIVE_MAX_BUTES 1              //最大接收字节数
unsigned char RecvBuf[16];                   //接收数据缓冲区
unsigned char RecvCount=0;                 //接收数据计数器
sbit T_TXD=P3^2;                               //发送数据引脚
sbit R_RXD=P3^3;                               //接收数据引脚
bit RXD_OK;                                        //数据接收完成标志,1接收正确 ,0接收错误

void delay104us()
{
   unsigned char i,j,k;
   for(i=1;i>0;i--)         // 注意后面没分号
   for(j=3;j>0;j--)         // 注意后面没分号
   for(k=189;k>0;k--);      // 注意后面有分号  
}
void delay52uS()                                      // 起始位结束后52uS采样数据
{
   unsigned char i,j,k;
   for(i=1;i>0;i--)         // 注意后面没分号
   for(j=3;j>0;j--)         // 注意后面没分号
   for(k=93;k>0;k--);       // 注意后面有分号  
}
voidsenbyte(unsigned char dat)
{
  unsigned char i=8;        //发送8位数据
  T_TXD =0;                   //发送起始位
  delay104us();        
  while(i--)
  {
    if(dat&1)  T_TXD=1;
    else T_TXD=0;
    delay104us();
    dat>>=1;
  }
  T_TXD=1;                       //发送停止位
  delay104us();
}

unsigned char recvbyte()
{
  unsigned char i;
  unsigned char dat=0;             //接收到的数据
  RXD_OK=0;                          //字节数据接收正常标志位
  delay52us();                         //数据位中心位置读取数据
  if(R_RXD==0)                      //确认起始位正常
  {
    delay104us();                     //起始位宽度
    for(i=0;i<8;i++)
    {
      if(R_RXD) dat|=(1<<i);
      delay104us();
    }
    if(R_RXD==1)                //确认停止位正常
    {
      RXD_OK=1;
    }
  }
  return dat;
}

void printfstr(char *pstr)        //串口打印字符串
{
  while(*pstr)
  {
    sendbyte(*pstr++);
  }
}
void main(void)
{
  unsigned char i;
  printfstr("模串口:STC15\r\n");
  while(1)
  {
    if(R_RXD==0)                                               //不断检测是否有起始位出现
    {
    recvbuf[recvcount]=recvbyte();
      if(RXD_OK ==1)                                          //一个字节接收正常
      {
        recvcount++;
        if(recvcount>=RECEIVE_MAX_BYTES)
        {
          recvcount=0;
          for(i=0;i<RECEIVE_MAX_BYTES;i++)
        {
          sendbyte( RecvBuf+1);                           //接收到的数据+1后发回
        }
        }
      }
    }
  }
}


这是一个IO引脚模拟串口通信的程序。
接收时先判断P3.3接收端口是否有起始位低电平出现,如有则按照低位在前的顺序接收8位数据,最后判断是否有停止位高电平出现,如有则完成一个字节的接收,否则继续等待。P3.2发送。
其中软件编写要严格按照异步通信的时序进行,每位传送时间按通信速率9600bps计算为(1/9600)s=104.2us。时钟:22.1184M。


这个程序也看了很久,重点是发送和接收函数。
发送函数比较好理解,接收函数不太容易。
迷惑的地方是: if(R_RXD) dat|=(1<<i);
                       1.为什么要左移,不是先发送低位的吗?左移以后先发送的不就成高位了?
                       2.为什么要dat为什么要或1,或1以后接收的值不就变了?


其实是没看明白这个语句:if(R_RXD) dat|=(1<<i);
                                       这样的格式很容易让我忽视if(R_RXD)去只思考dat|=(1<<i);
                                       没有实践就没有发言权,抛弃条件去思考结果,就是耍流氓,肯定是思考不出结果的。
                                       这条语句说的是如果R_RXD==1,那么dat当前为就置1。
                                       另外1<<i,是指1左移i位,而不是i左移1位,同样的错误真的很容易再犯,习惯性思维害人。


另:
#define RECEIVE_MAX_BUTES 1              //最大接收字节数


这条宏语句的值改成2后,输出的结果并不是想像的那样。
例如我输入11 22 点击发送,我认为会回复:12 23;实际上第一次点击发送时串口助手是没有接收信息的(接收窗口空白),点击第二次才会接收到正确回复。
这是因为         
    for(i=0;i<RECEIVE_MAX_BYTES;i++)
        {
          sendbyte( RecvBuf+1);                           //接收到的数据+1后发回        }
这条语句的原因。

改成条件语句:
        SendByte(RecvBuf+1);           // 接收到得数据+1后发回
                                                i++;
                                                if(i>=RECEIVE_MAX_BYTES)
                                                {
                                                        i=0;
                                                }
自认为会完成改善,其实结果是点击一次发送,接收到的只有一个字节的内容,第一次接收到12,第二次接收到23,再点就是12,再是23。

经思考:如果想点击一次发送,接收到所有发送内容,需要增加发送数组函数,调用现有发送函数将发送内容存储到数组中,main()函数中调用发送数组函数。语句没有写,所以暂时只用语言描述。
                                       
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:299910 发表于 2025-2-19 09:30 | 只看该作者
模拟串口就是把串口协议的帧格式用程序语句表达出来,然后放到IO口上去。这里说的帧格式就是信号在固定波特率的高低电平变化,即1,0的变化和波形宽度。帧格式还包含帧内每部分信号的含义,如起始位,数据,校验,停止位,当然也都是1,0的变化和波形宽度。该程序没有校验部分。
回复

使用道具 举报

板凳
ID:1096929 发表于 2025-2-20 15:22 | 只看该作者
jackduan2018 发表于 2025-2-19 09:30
模拟串口就是把串口协议的帧格式用程序语句表达出来,然后放到IO口上去。这里说的帧格式就是信号在固定波特 ...

解读的非常好!
回复

使用道具 举报

地板
ID:624769 发表于 2025-3-5 10:30 | 只看该作者
你既然用的 STC 单片机, IO 模拟串口,还是看 STC 官方例程吧, 你手上这个,不说毫无实用性吧,还会把编程思路带歪,将来写程序,只会单线程思考。
回复

使用道具 举报

5#
ID:446156 发表于 2025-3-10 11:33 | 只看该作者
不建议用IO模拟串口时序,更不建议应用到项目中,纯粹是吃饱了找罪受
回复

使用道具 举报

6#
ID:108361 发表于 2025-4-17 13:25 | 只看该作者
稍微复杂的程序,实时性要求上来一点,这个IO模拟串口就容易翻车,现在串口成本很低了,不再是那个MCS51的时代了,需要与时俱进一点
回复

使用道具 举报

7#
ID:513213 发表于 2025-4-30 09:05 | 只看该作者
qinlu123 发表于 2025-3-10 11:33
不建议用IO模拟串口时序,更不建议应用到项目中,纯粹是吃饱了找罪受

串口不够时,可以备用。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表