找回密码
 立即注册

QQ登录

只需一步,快速开始

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

关于STC8单片机模拟I2C的程序问题

[复制链接]
跳转到指定楼层
楼主
ID:475845 发表于 2019-7-12 16:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我是用的STC8A8K64S4A12这个单片机,我想的是通过模拟I2C来控制另一块显示板。在开始我硬件I2C,但是并没有成功,而且好像这一款硬件I2C不怎么好用,我就想着用模拟的,但还是没有成功,下面我把代码贴上来,希望大佬能够指点一下。

单片机源程序如下:
#include <intrins.h>
#include <STC8F.H>

//定义I2C总线
sbit SCL = P1^1;//时钟线
sbit SDA = P1^0;//数据线
sbit LED = P2^7;

unsigned char KEY;

//函数声明
void delay1();

void IICdelay();//短暂延时
void IICinit();//总线初始化
void IICstart();//产生起始信号
void IICstop();//产生停止信号
unsigned char SlaRes();//从设备应答
void MasRes();//主设备应答
void NoRes();//主设备不应答
void IICwrite(unsigned char iicdata);//写一个字节
unsigned char IICread();//读一个字节

void LED_OUT();
unsigned char KEY_V();

//I2C通信使用delay()函数
//一个_nop_()延时1us,总线要求延时大于4.7us
//单片机频率执行周期不同时按实际需要修改
void IICdelay()
{
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
}

void delay1s()
{
    unsigned char i, j, k;

    _nop_();
    _nop_();
    i = 43;
    j = 6;
    k = 203;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

//总线初始化
//将总线都拉高以释放总线
void IICinit()
{
        SCL = 1;
        IICdelay();
        SDA = 1;
        IICdelay();
}

//启动信号
//SCL在高电平期间,SDA由高电平向低电平的变化定义为启动信号
void IICstart()
{
        SDA = 1;
        IICdelay();
        SCL = 1;
        IICdelay();
        SDA = 0;
        IICdelay();
          SCL = 0;
          IICdelay();
}

//停止信号
//SCL在高电平期间,SDA由低电平向高电平的变化定义为停止信号
void IICstop()
{
        SDA = 0;
        IICdelay();
        SCL = 1;
        IICdelay();
        SDA = 1;
        IICdelay();
}

//应答信号
//SCL在高电平期间,SDA被从设备拉为低电平表示应答
//其中(SDA == 1)&&(i<255)表示若在一段时间内没有收到从器件的应答则主器件默认从器件已经收到数据而不再等待应答信号
unsigned char SlaRes()
{
        unsigned char i = 0;
        
        SCL = 1;
        IICdelay();
        while(SDA)
        {
                i++;
                if(i > 254)
                {
                        IICstop();
                        return 1;
                }
        }
        SCL = 0;
        IICdelay();
        
        return 0;
}

//由主设备发出应答
//从设备在发送完一字节后将SDA拉高(SDA被释放),主设备将SDA拉低产生应答
void MasRes()
{
        SDA = 0;//直接将SDA拉低,再操纵SCL
        IICdelay();
        SCL = 1;
        IICdelay();
        SCL = 0;
        IICdelay();
}

//主设备不应答
void NoRes()
{
        SDA = 1;//SDA高电平为不应答
        IICdelay();
        SCL = 1;
        IICdelay();
        SCL = 0;
        IICdelay();
}

//写入
//表示将temp左移一位,最高位将移入PSW寄存器的CY位中,然后将CY赋给SDA进而在SCL的控制下发送出去
//最后将SDA拉高,以等待从设备产生应答
void IICwrite(unsigned char iicdata)
{
        unsigned char i;
        for(i=0; i<8; i++)
        {
                iicdata = iicdata << 1;
                SCL = 0;
                IICdelay();
                SDA = CY;
                IICdelay();
                SCL = 1;
                IICdelay();
        }
        SCL = 0;
        IICdelay();
        SDA = 1;
        IICdelay();
}

//读出
//串行接受一个字节时需将8位一位一位地接受,然后再组合成一个字节,
unsigned char IICread()
{
        unsigned char i, k;
        SCL = 0;
        IICdelay();
        SDA = 1;
        IICdelay();
        for(i=0; i<8; i++)
        {
                SCL = 1;
                IICdelay();
                k = (k<<1)|SDA;
                SCL = 0;
                IICdelay();
        }
        IICdelay();
        return k;
}



void LED_OUT()
{
    IICstart();                                   //发送起始命令
      IICwrite(0xa0);                             //发送设备地址+写命令
    SlaRes();
      IICwrite(0x04);                             //发送存储地址高字节
    SlaRes();
      IICwrite(0x00);                             //发送存储地址低字节
    SlaRes();
      IICwrite(0x00);                             //写测试数据1
    SlaRes();
      IICwrite(0x01);                             //写
    SlaRes();
      IICstop();                                                                        //停止信号
}


unsigned char KEY_V()
{

    IICstart();                                   //发送起始命令
      IICwrite(0xa0);                             //发送设备地址+写命令
    SlaRes();
      IICwrite(0x00);                             //发送存储地址高字节
    SlaRes();
    IICstart();                                   //发送起始命令
      IICwrite(0x0F);                             //发送设备地址+读命令
    SlaRes();
      KEY = IICread();                               //读取数据
    NoRes();                                      //非应答信号
        IICstop();                                    //发送停止命令
    return KEY;
}


void main()
{
    IICinit();
   
   
   while(1)
     {
    KEY_V();
         {
            if(KEY != 0X00)
                {
                  LED_OUT();
                    LED=~LED;
                    delay1s();
                    delay1s();
                    delay1s();
                    delay1s();
                }
         }
     }
}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:401564 发表于 2019-7-13 11:41 | 只看该作者
从机的应答信号不能用延时,而且,你延时的时间太短了
从机的应答信号应该是:先释放SDA,然后再不断的去检测SDA,一直要等到SDA出现低电平才能视为应答信号
如果是怕死循环,可以用看门狗或者中断或者是设定检测次数之类,到时间就退出循环就可以了
回复

使用道具 举报

板凳
ID:475845 发表于 2019-7-15 15:45 | 只看该作者
Y_G_G 发表于 2019-7-13 11:41
从机的应答信号不能用延时,而且,你延时的时间太短了
从机的应答信号应该是:先释放SDA,然后再不断的去检测S ...

这个程序大体是没有问题的吧,我把那个位置的改了下,但是还是没有解决问题.因为之前没有弄过这个,延时的话是看网上说的要4.7us,一个Nop()相当于1us,就用的这个延时,主程序里面自己是用的另外一个
回复

使用道具 举报

地板
ID:475845 发表于 2019-7-15 15:49 | 只看该作者
本帖最后由 Ty78619120 于 2019-7-15 18:43 编辑
Y_G_G 发表于 2019-7-13 11:41
从机的应答信号不能用延时,而且,你延时的时间太短了
从机的应答信号应该是:先释放SDA,然后再不断的去检测S ...

这个程序大体上应该没有问题吧?我把你说的位置改了试了下,但是还是没有解决问题,我自己在试一下。自己之前没有弄过这个东西,刚玩单片机不久。看网上别说延时大于4.7us就行了,一个nop()相当于1us,自己主程序里面是用的另外一个延时。
回复

使用道具 举报

5#
ID:155507 发表于 2019-7-15 20:43 | 只看该作者
我给你来个STC8单片机模拟 的I2C程序试试


  1. /****************************/
  2. void        I2C_Delay(void)        //for 1T  STC        delay
  3. {
  4.         uint16 j,i;   
  5.         for(j=0;j<2;j++)   
  6.         {   
  7.                 for(i=0;i<7;i++);   
  8.         }  
  9. }

  10. /****************************/
  11. void I2C_Start(void)               //start the I2C, SDA High-to-low when SCL is high
  12. {
  13.         SDA = 1;
  14.         I2C_Delay();
  15.         SCL = 1;
  16.         I2C_Delay();
  17.         SDA = 0;
  18.         I2C_Delay();
  19.         SCL = 0;
  20.         I2C_Delay();
  21. }               

  22. /****************************/
  23. void I2C_Stop(void)                                        //STOP the I2C, SDA Low-to-high when SCL is high
  24. {
  25.         SDA = 0;
  26.         I2C_Delay();
  27.         SCL = 1;
  28.         I2C_Delay();
  29.         SDA = 1;
  30.         I2C_Delay();
  31. }

  32. /****************************/
  33. void S_ACK(void)              //Send ACK (LOW)
  34. {
  35.         SDA = 0;
  36.         I2C_Delay();
  37.         SCL = 1;
  38.         I2C_Delay();
  39.         SCL = 0;
  40.         I2C_Delay();
  41. }

  42. /****************************/
  43. void S_NoACK(void)           //Send No ACK (High)
  44. {
  45.         SDA = 1;
  46.         I2C_Delay();
  47.         SCL = 1;
  48.         I2C_Delay();
  49.         SCL = 0;
  50.         I2C_Delay();
  51. }

  52. /****************************/
  53. void I2C_Check_ACK(void)         //Check ACK, If F0=0, then right, if F0=1, then error
  54. {
  55.         SDA = 1;
  56.         I2C_Delay();
  57.         SCL = 1;
  58.         I2C_Delay();
  59.         F0  = SDA;
  60.         SCL = 0;
  61.         I2C_Delay();
  62. }

  63. /****************************/
  64. void I2C_WriteAbyte(uint8 dat)                //write a byte to I2C
  65. {
  66.         uint8 i;
  67.         i = 8;
  68.         do
  69.         {
  70.                 if(dat & 0x80)        SDA = 1;
  71.                 else                        SDA        = 0;
  72.                 dat <<= 1;
  73.                 I2C_Delay();
  74.                 SCL = 1;
  75.                 I2C_Delay();
  76.                 SCL = 0;
  77.                 I2C_Delay();
  78.         }
  79.         while(--i);
  80. }

  81. /**************************************
  82. 功能描述:向指定地址写数据
  83. 入口参数:uint8 addr, uint8 *p, uint8 number
  84. 返回值:无
  85. ***************************************/
  86. uint8 I2C_ReadAbyte(void)                        //read A byte from I2C
  87. {
  88.         uint8 i,dat;
  89.         i = 8;
  90.         SDA = 1;
  91.         do
  92.         {
  93.                 SCL = 1;
  94.                 I2C_Delay();
  95.                 dat <<= 1;
  96.                 if(SDA)                dat++;
  97.                 SCL  = 0;
  98.                 I2C_Delay();
  99.         }
  100.         while(--i);
  101.         return(dat);
  102. }

  103. /**************************************
  104. 功能描述:向指定地址写数据
  105. 入口参数:uint8 addr, uint8 *p, uint8 number
  106. 返回值:无
  107. ***************************************/
  108. void WriteNbyte(uint8 addr, uint8 *p, uint8 number)                        /*        WordAddress,First Data Address,Byte lenth        */
  109.                                                                                                  //F0=0,right, F0=1,error
  110. {
  111.         I2C_Start();
  112.         I2C_WriteAbyte(SLAW);
  113.         I2C_Check_ACK();
  114.         if(!F0)
  115.         {
  116.                 I2C_WriteAbyte(addr);
  117.                 I2C_Check_ACK();
  118.                 if(!F0)
  119.                 {
  120.                         do
  121.                         {
  122.                                 I2C_WriteAbyte(*p);                p++;
  123.                                 I2C_Check_ACK();
  124.                                 if(F0)        break;
  125.                         }
  126.                         while(--number);
  127.                 }
  128.         }
  129.         I2C_Stop();
  130. }


  131. /**************************************
  132. 功能描述:向指定地址读取数据
  133. 入口参数:uint8 addr, uint8 *p, uint8 number
  134. 返回值:无
  135. ***************************************/
  136. void ReadNbyte(uint8 addr, uint8 *p, uint8 number)                /*        WordAddress,First Data Address,Byte lenth        */
  137. {
  138.         I2C_Start();
  139.         I2C_WriteAbyte(SLAW);
  140.         I2C_Check_ACK();
  141.         if(!F0)
  142.         {
  143.                 I2C_WriteAbyte(addr);
  144.                 I2C_Check_ACK();
  145.                 if(!F0)
  146.                 {
  147.                         I2C_Start();
  148.                         I2C_WriteAbyte(SLAR);
  149.                         I2C_Check_ACK();
  150.                         if(!F0)
  151.                         {
  152.                                 do
  153.                                 {
  154.                                         *p = I2C_ReadAbyte();        p++;
  155.                                         if(number != 1)                S_ACK();        //send ACK
  156.                                 }
  157.                                 while(--number);
  158.                                 S_NoACK();                        //send no ACK
  159.                         }
  160.                 }
  161.         }
  162.         I2C_Stop();
  163. }



  164. /*********************************END FILE********************************************/       

复制代码
回复

使用道具 举报

6#
ID:401564 发表于 2019-7-16 08:18 | 只看该作者
Ty78619120 发表于 2019-7-15 15:49
这个程序大体上应该没有问题吧?我把你说的位置改了试了下,但是还是没有解决问题,我自己在试一下。自己 ...

C不会,只能看一个大概.
但IIC协议是知道的,IIC的时序很重要:主机的应答可以用延时,从机的应答最好是等待,一直等到从机自己产生应答信号为止.
STC的指令速度和传统的8051是不同的,要快很多倍的,最快的是快12倍,所以,你那延时可能是1uS都没有

在程序最初的调试阶段,你可以暂时不要理会速度问题,先把所有的延时都用到100mS,把程序调试好了,再慢慢的去调试延时
回复

使用道具 举报

7#
ID:475845 发表于 2019-7-16 11:28 | 只看该作者
Y_G_G 发表于 2019-7-16 08:18
C不会,只能看一个大概.
但IIC协议是知道的,IIC的时序很重要:主机的应答可以用延时,从机的应答最好是等待 ...

嗯嗯,好的,谢谢帮助,我试一下
回复

使用道具 举报

8#
ID:475845 发表于 2019-7-16 11:29 | 只看该作者
angmall 发表于 2019-7-15 20:43
我给你来个STC8单片机模拟 的I2C程序试试

谢谢了,我用着试一下
回复

使用道具 举报

9#
ID:371527 发表于 2019-7-21 11:52 | 只看该作者
STC8a8k比STC15的指令执行速度还要快,延时时间,不能照搬以前的一个Nop用1us的概念,最好示波器看一下,即便用ISP生成的延时软件,也要对应相应的MCU STC ,最好是用Mosc相应公式做延时程序,再用示波器观察延时是否正确。
回复

使用道具 举报

10#
ID:307020 发表于 2019-7-23 12:25 | 只看该作者
论坛里有PCF8574 I2C 转并口驱动LCD1602液晶显示屏的例程,在左上侧的“请输入搜索内容”后边输入PCF8547就可以看到;且为模块化很好。
回复

使用道具 举报

11#
ID:975054 发表于 2021-12-7 19:49 | 只看该作者
还是用硬件I2C简约一点吧,近期一直在研究
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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