找回密码
 立即注册
楼主: zhouq

第1篇_8H1K17 I2C从机如何/何时发送ACK/NACK

[复制链接]
  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-06-16 08:41:37
已绑定手机

67

主题

1913

回帖

4297

积分

荣誉版主

无情的代码机器

积分
4297
发表于 2025-3-18 18:47:31 来自手机 | 显示全部楼层
zhouq 发表于 2025-3-18 18:21
按照你数据手册从机中断的代码例程,当我发送完设备地址后(读寄存器),此时给I2CTXD赋值,等我再发寄存器 ...

赋值完这个时候按我理解SDA线控制权应该在从机,从机回数据用呀。你上传下你测试用的主从机代码和电路图么
三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:68
  • 最近打卡:2025-06-16 09:36:30
已绑定手机

12

主题

29

回帖

418

积分

中级会员

积分
418
发表于 2025-3-18 18:50:52 | 显示全部楼层
erci*** 发表于 2025-3-18 18:47
赋值完这个时候按我理解SDA线控制权应该在从机,从机回数据用呀。你上传下你测试用的主从机代码和电路图 ...

I2C规范中,当主机读从机数据时,SDA控制权需要转交给从机。回帖中有我写寄存器的代码截图
花有重开日,人无再少年
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:68
  • 最近打卡:2025-06-16 09:36:30
已绑定手机

12

主题

29

回帖

418

积分

中级会员

积分
418
发表于 2025-3-18 18:53:16 | 显示全部楼层
erci*** 发表于 2025-3-18 18:11
图里不是收到主机事件,将数据放到寄存器了嘛,放了SDA引脚应该就有反应了。

“图里不是收到主机事件,将数据放到寄存器了嘛,放了SDA引脚应该就有反应了。”
“放了SDA引脚应该就有反应了”我猜测也是这样,数据放到了SDA后,主机再发时钟信号,此时STC从机就会在SDA引脚动作。
花有重开日,人无再少年
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-06-16 08:41:37
已绑定手机

67

主题

1913

回帖

4297

积分

荣誉版主

无情的代码机器

积分
4297
发表于 2025-3-18 19:41:14 | 显示全部楼层
zho*** 发表于 2025-3-18 17:45
主机发送完设备地址,寄存器地址,之后再发送起始信号,然后呢?从机通过什么方式把数据放到SDA引脚上?
...

只有这一张截图吗?操作SCL是主机代码啊,你的从机代码呢?

“等我再发寄存器地址(此时主机在发SCL)时,从机就会和主机抢SDA线的控制权”
这句话又是什么意思呢,从机发什么寄存器地址,从机直接发数据么,主机此时发SCL和SDA线收数据同时进行。
三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:99
  • 最近打卡:2025-06-16 09:28:47

733

主题

1万

回帖

1万

积分

管理员

积分
16635
发表于 2025-3-18 21:10:25 | 显示全部楼层
1, I/O 设置成开漏,外部加10K 上拉电阻,或提前打开内部上拉电阻
2,这有现成的参考程序
截图202503182110044082.jpg

#include "reg51.h"
#include "intrins.h"

sfr     P_SW2   =   0xba;

#define I2CCFG      (*(unsigned char volatile xdata *)0xfe80)
#define I2CMSCR     (*(unsigned char volatile xdata *)0xfe81)
#define I2CMSST     (*(unsigned char volatile xdata *)0xfe82)
#define I2CSLCR     (*(unsigned char volatile xdata *)0xfe83)
#define I2CSLST     (*(unsigned char volatile xdata *)0xfe84)
#define I2CSLADR    (*(unsigned char volatile xdata *)0xfe85)
#define I2CTXD      (*(unsigned char volatile xdata *)0xfe86)
#define I2CRXD      (*(unsigned char volatile xdata *)0xfe87)

sfr     P1M1    =   0x91;
sfr     P1M0    =   0x92;
sfr     P0M1    =   0x93;
sfr     P0M0    =   0x94;
sfr     P2M1    =   0x95;
sfr     P2M0    =   0x96;
sfr     P3M1    =   0xb1;
sfr     P3M0    =   0xb2;
sfr     P4M1    =   0xb3;
sfr     P4M0    =   0xb4;
sfr     P5M1    =   0xc9;
sfr     P5M0    =   0xca;

sbit    SDA     =   P1^4;
sbit    SCL     =   P1^5;

bit isda;                                       //设备地址标志
bit isma;                                       //存储地址标志
unsigned char addr;
unsigned char pdata buffer[256];

void I2C_Isr() interrupt 24
{
    _push_(P_SW2);
    P_SW2 |= 0x80;

    if (I2CSLST & 0x40)
    {
        I2CSLST &= ~0x40;                       //处理START事件
    }
    else if (I2CSLST & 0x20)
    {
        I2CSLST &= ~0x20;                       //处理RECV事件
        if (isda)
        {
            isda = 0;                           //处理RECV事件(RECV DEVICE ADDR)
        }
        else if (isma)
        {
            isma = 0;                           //处理RECV事件(RECV MEMORY ADDR)
            addr = I2CRXD;
            I2CTXD = buffer[addr];
        }
        else
        {
            buffer[addr++] = I2CRXD;            //处理RECV事件(RECV DATA)
        }
    }
    else if (I2CSLST & 0x10)
    {
        I2CSLST &= ~0x10;                       //处理SEND事件
        if (I2CSLST & 0x02)
        {
            I2CTXD = 0xff;                      //接收到NAK则停止读取数据
        }
        else
        {
            I2CTXD = buffer[++addr];            //接收到ACK则继续读取数据
        }
    }
    else if (I2CSLST & 0x08)
    {
        I2CSLST &= ~0x08;                       //处理STOP事件
        isda = 1;
        isma = 1;
    }

    _pop_(P_SW2);
}

void main()
{
    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;

    P_SW2 = 0x80;

    I2CCFG = 0x81;                              //使能I2C从机模式
    I2CSLADR = 0x5a;                            //设置从机设备地址寄存器I2CSLADR=0101_1010B
                                                //即I2CSLADR[7:1]=010_1101B,MA=0B。
                                                //由于MA为0,主机发送的的设备地址必须与
                                                //I2CSLADR[7:1]相同才能访问此I2C从机设备。
                                                //主机若需要写数据则要发送5AH(0101_1010B)
                                                //主机若需要读数据则要发送5BH(0101_1011B)
    I2CSLST = 0x00;
    I2CSLCR = 0x78;                             //使能从机模式中断
    EA = 1;

    isda = 1;                                   //用户变量初始化
    isma = 1;
    addr = 0;
    I2CTXD = buffer[addr];

    while (1);
}


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:68
  • 最近打卡:2025-06-16 09:36:30
已绑定手机

12

主题

29

回帖

418

积分

中级会员

积分
418
发表于 2025-3-19 09:46:31 | 显示全部楼层
erci*** 发表于 2025-3-18 19:41
只有这一张截图吗?操作SCL是主机代码啊,你的从机代码呢?

“等我再发寄存器地址(此时主机在发SCL)时 ...
  1. 这是我的从机代码
复制代码


花有重开日,人无再少年
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:68
  • 最近打卡:2025-06-16 09:36:30
已绑定手机

12

主题

29

回帖

418

积分

中级会员

积分
418
发表于 2025-3-19 09:47:24 | 显示全部楼层
这是我的从机代码,大家看下有无问题
  1. /* Includes ------------------------------------------------------------------*/
  2. #include "HeaderInc.h"
  3. /* Private define-------------------------------------------------------------*/
  4. #define User_I2C_ADDR  0x30          //从机地址为0x30,高7位,MA=0,MA=0表示设备地址必须与I2CSLADR[7:1]相同
  5. #define WRMask         0x01          //读写操作掩码
  6. #define ADDR_IAP_17A   0x8A   //IAP升级芯片地址
  7. //I2C配置寄存器(I2CCFG)
  8. //使能I2C从机模式
  9. //B7-B6 10
  10. #define EnI2C_Slave    0x80
  11. //使能I2C主机模式
  12. //B7-B6 11
  13. //#define EnI2C_Master   0xC0
  14. //MSSPEED[5:0],I2C在主机模式下,MSSPEED设置才有效
  15. //I2C总线速度=FOSC / 2 / (MSSPEED * 2 + 4),设置总线速度为400K,那么MSSPEED=13
  16. //#define I2C_Speed      0x0D
  17. //I2C 从机状态寄存器(I2CSLST)
  18. #define I2C_S_BUSY       0x80          //从机状态位,0空闲,1忙,只读
  19. #define I2C_S_STAIF      0x40          //收到START信号的中断请求位
  20. #define I2C_S_RXIF       0x20          //从机收到1字节的数据的中断请求位
  21. #define I2C_S_TXIF       0x10          //从机发完1字节的数据的中断请求位
  22. #define I2C_S_STOIF      0x08          //从机收到STOP信号的中断请求位
  23. #define I2C_S_ACKI       0x02          //从机接收到的ACK数据
  24. #define I2C_S_ACKO       0x01          //从机准备要发送的ACK信号
  25. /* Private variables----------------------------------------------------------*/
  26. /* Private function prototypes------------------------------------------------*/      
  27. //声明使用的函数
  28. static void     I2C_S_Init(void);        //I2C初始化
  29. /* Public variables-----------------------------------------------------------*/
  30. //创建并初始化结构体
  31. I2C_S_t idata I2C_S =
  32. {
  33.         FALSE,      /* DevAddrFlag - 初始化时未接收到设备地址,即未收到任何数据 */
  34.         FALSE,      /* RegAddrFlag - 初始化时未接收到寄存器地址 */
  35.         FALSE,            /* 读写标志位,TRUE 表示读操作,FALSE 表示写操作 */
  36.         0,          /* DevAddr */
  37.         0,          /* RegAddr */
  38.         I2C_S_Init  /* Init */
  39. };
  40. /*
  41.         * @name   I2C_S_Init
  42.         * @brief  I2C初始化
  43.         * @param  None
  44.         * @retval None      
  45. */
  46. static void I2C_S_Init(void)
  47. {
  48.         //设置P14 P15为 I2C 功能
  49.     P_SW2 |= 0x80;
  50.        
  51.     //设置从机设备地址寄存器I2CSLADR=0011_0000B
  52.         //B0为MA,设置为0表示设备地址必须与I2CSLADR[7:1]相同
  53.         I2CSLADR = User_I2C_ADDR;                        
  54.         //配置从机状态为空闲,并清零所有中断标志
  55.         I2CSLST = 0x00;
  56.     //使能I2C从机模式,此寄存器涉及到端口
  57.         I2CCFG = EnI2C_Slave;
  58. }
  59. /*
  60.         * @name   I2C_INTR()
  61.         * @brief  I2C中断函数
  62.         * @param  参数说明
  63.         * @retval None      
  64. */
  65. void I2C_INTR() interrupt 24
  66. {               
  67.         //收到START信号
  68.     if (I2CSLST & I2C_S_STAIF)
  69.     {
  70.         I2CSLST &= ~I2C_S_STAIF;//清零STAIF标志位
  71.                        
  72.     }//从机收到了1字节数据
  73.     else if (I2CSLST & I2C_S_RXIF)
  74.     {
  75.         I2CSLST &= ~I2C_S_RXIF;//清零RXIF标志位
  76.                        
  77.                 //接收设备地址
  78.         if (I2C_S.DevAddrFlag == FALSE)//如果设备标志位为FALSE,表示这是收到的第1字节                     
  79.         {       
  80.                         I2C_S.DevAddr = I2CRXD;//接收设备地址
  81.                        
  82.                         //下面代码多余了,MA=0时设备地址只有和I2CSLADR[7:1]相同才会处理数据,留着也无妨
  83.                         //第1字节数据是设备地址数据
  84.                         if((I2C_S.DevAddr & 0xFE) == User_I2C_ADDR)
  85.                         {
  86.                                 I2C_S.DevAddrFlag = TRUE;//处理RECV事件(RECV DEVICE ADDR)       
  87.                                 if (I2C_S.DevAddr & WRMask) {
  88.                                         I2C_S.ReadWriteFlag = TRUE;// 读操作
  89.                                 } else {
  90.                                         I2C_S.ReadWriteFlag = FALSE;// 写操作
  91.                                 }       
  92.                         }                       
  93.         }//接收寄存器地址,已经收到过设备地址,且I2C_S.RegAddrFlag为 FALSE,则表示此次数据是寄存器地址
  94.         else if ((I2C_S.DevAddrFlag == TRUE) && (I2C_S.RegAddrFlag == FALSE))                          
  95.         {
  96.             I2C_S.RegAddrFlag = TRUE;//处理RECV事件(RECV REG ADDR)       
  97.                         I2C_S.RegAddr = I2CRXD;//接收寄存器地址
  98.         }//读寄存器
  99.                 else if(I2C_S.ReadWriteFlag == TRUE)
  100.                 {
  101.                         //当前只读四个通道的值
  102.                         if (I2C_S.RegAddr == ChA_ID ||
  103.                                 I2C_S.RegAddr == ChB_ID ||
  104.                                 I2C_S.RegAddr == ChC_ID ||
  105.                                 I2C_S.RegAddr == ChD_ID)
  106.                         {
  107.                                 ChxDetec.BitValue = ChxDetec.GetBitValue(I2C_S.RegAddr);
  108.                                 I2CTXD = (uint8_t)(ChxDetec.BitValue >> 8);//上传低位的ADC值
  109.                         }
  110.                         else if(I2C_S.RegAddr == ADDR_IAP_17A)
  111.                         {
  112.                         }
  113.                 }//写寄存器
  114.                 else if(I2C_S.ReadWriteFlag == FALSE)
  115.                 {
  116.                         if(I2C_S.RegAddr == ADDR_IAP_17A)
  117.                         {
  118.                                 //buffer[i++] = I2CRXD;
  119.                         }
  120.                 }               
  121.                                
  122.     }//从机发送完了1字节数据
  123.     else if (I2CSLST & I2C_S_TXIF)                                                                               
  124.     {
  125.         I2CSLST &= ~I2C_S_TXIF;//清零TXIF标志位
  126.                
  127.                 if(I2CSLST & I2C_S_ACKI)//如果ACKI=1,说明主机NACK了
  128.                 {
  129.                         //主机NACK停止发送数据
  130.                
  131.                 }//接收到ACK
  132.                 else
  133.                 {
  134.                         if (I2C_S.RegAddr == ChA_ID ||
  135.                                 I2C_S.RegAddr == ChB_ID ||
  136.                                 I2C_S.RegAddr == ChC_ID ||
  137.                                 I2C_S.RegAddr == ChD_ID)
  138.                         {
  139.                                 ChxDetec.BitValue = ChxDetec.GetBitValue(I2C_S.RegAddr);
  140.                                 I2CTXD = (uint8_t)(ChxDetec.BitValue & 0x00FF);//上传低位的ADC值
  141.                         }
  142.                         else if(I2C_S.RegAddr == ADDR_IAP_17A)
  143.                         {
  144.                                 //I2CTXD = 0;
  145.                         }
  146.                        
  147.                 }
  148.     }//从机收到STOP信号
  149.     else if (I2CSLST & I2C_S_STOIF)
  150.     {
  151.         I2CSLST &= ~I2C_S_STOIF;//清零STOIF标志位  
  152.                        
  153.         I2C_S.DevAddrFlag = FALSE;//将设备地址标志位恢复默认值
  154.         I2C_S.RegAddrFlag = FALSE;//将寄存器地址标志位恢复默认值
  155.                 I2C_S.ReadWriteFlag = FALSE;               
  156.                 I2C_S.DevAddr = 0;
  157.                 I2C_S.RegAddr = 0;
  158.     }
  159. }
复制代码

点评

还是不完整,用的中断模式,没见开I2CSLCR中断。 你用例程能跑通吗? 另外sda,scl用逻辑分析仪抓包没有,什么现象呢?  详情 回复 发表于 2025-3-19 12:02
花有重开日,人无再少年
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-06-16 08:41:37
已绑定手机

67

主题

1913

回帖

4297

积分

荣誉版主

无情的代码机器

积分
4297
发表于 2025-3-19 12:02:47 | 显示全部楼层
zho*** 发表于 2025-3-19 09:47
这是我的从机代码,大家看下有无问题

还是不完整,用的中断模式,没见开I2CSLCR中断。
你用例程能跑通吗?
另外sda,scl用逻辑分析仪抓包没有,什么现象呢?
三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:68
  • 最近打卡:2025-06-16 09:36:30
已绑定手机

12

主题

29

回帖

418

积分

中级会员

积分
418
发表于 2025-3-19 13:40:00 | 显示全部楼层
erci*** 发表于 2025-3-19 12:02
还是不完整,用的中断模式,没见开I2CSLCR中断。
你用例程能跑通吗?
另外sda,scl用逻辑分析仪抓包没有 ...

在别的文件配置了:
        //I2C从机模式的各种中断使能
        I2CSLCR = 0x78;

还没开始验证,我猜测芯片例程应该没问题,I2CRXT被赋值后,接收到SCL时钟信号就会把数据发出去。我在STC8G1K17A上成功实现过I2C功能,但只有设备地址,没有寄存器地址的读写

点评

注意I2CRXD 是收,I2CTXD才是发送寄存器。  详情 回复 发表于 2025-3-19 13:46
花有重开日,人无再少年
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-06-16 08:41:37
已绑定手机

67

主题

1913

回帖

4297

积分

荣誉版主

无情的代码机器

积分
4297
发表于 2025-3-19 13:46:37 | 显示全部楼层
zho*** 发表于 2025-3-19 13:40
在别的文件配置了:
        //I2C从机模式的各种中断使能
        I2CSLCR = 0x78;

注意I2CRXD 是收,I2CTXD才是发送寄存器。
三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-6-16 13:01 , Processed in 0.378720 second(s), 104 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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