zhouq 发表于 2025-3-18 16:46:53

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

我的8H1K17 I2C作为从机,如果主机要读寄存器,是直接将数据赋值给I2CTXD,它就会自动启动发送数据给主机,是吗?还是说要做其他设置

神农鼎 发表于 2025-3-18 21:10:25

1, I/O 设置成开漏,外部加10K 上拉电阻,或提前打开内部上拉电阻
2,这有现成的参考程序


#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;

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;
      }
      else
      {
            buffer = 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=010_1101B,MA=0B。
                                                //由于MA为0,主机发送的的设备地址必须与
                                                //I2CSLADR相同才能访问此I2C从机设备。
                                                //主机若需要写数据则要发送5AH(0101_1010B)
                                                //主机若需要读数据则要发送5BH(0101_1011B)
    I2CSLST = 0x00;
    I2CSLCR = 0x78;                           //使能从机模式中断
    EA = 1;

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

    while (1);
}


DebugLab 发表于 2025-3-18 17:30:19

从机是把数据放到寄存器然后等主机来读,通信需要时钟,从机又不能主动发时钟

zhouq 发表于 2025-3-18 17:42:44

DebugLab 发表于 2025-3-18 17:30
从机是把数据放到寄存器然后等主机来读,通信需要时钟,从机又不能主动发时钟 ...
我设置的是主机先发送设备地址,再发送寄存器地址,然后读取I2CTXD的值,那主机要怎么读I2CTXD?STC8H将值赋值给I2CTXD,它什么时候会把逻辑值推送到SDA引脚?收到起始信号,就把I2CTXD的逻辑值推送到SDA引脚吗?

zhouq 发表于 2025-3-18 17:45:55

主机发送完设备地址,寄存器地址,之后再发送起始信号,然后呢?从机通过什么方式把数据放到SDA引脚上?

ercircle 发表于 2025-3-18 17:56:21

参考手册例程:

zhouq 发表于 2025-3-18 18:05:48

ercircle 发表于 2025-3-18 17:56
参考手册例程:

我发的图就是手册的这一部分

ercircle 发表于 2025-3-18 18:11:59

图里不是收到主机事件,将数据放到寄存器了嘛,放了SDA引脚应该就有反应了。


你是想问怎么绑定SDA引脚?

zhouq 发表于 2025-3-18 18:13:58

按照一楼的说法,看来STC8H1K17不支持寄存器读取的操作,因为你一发读写操作,就要给I2CTXD赋值,此时我主机还没把寄存器地址发送过去呢,但即使是这种情况,仍然可以实现我的目的,我只需要将MA设置为1,设置从机接受所有设备地址,然后把我的不同的寄存器地址统统设置为不同的设备地址,这样只要我一读设备地址,从机就给我发数据

zhouq 发表于 2025-3-18 18:16:23

ercircle 发表于 2025-3-18 18:11
图里不是收到主机事件,将数据放到寄存器了嘛,放了SDA引脚应该就有反应了。




I2C中读数据分三步,第一步发送设备地址,第二步发送寄存器地址,第三步发送时钟信号,此时主机去读SDA线上的电平。我想问的就是第三步,我要怎么操作STC单片机(从机),它会在第三步把逻辑电平推送到SDA引脚

zhouq 发表于 2025-3-18 18:21:01

按照你数据手册从机中断的代码例程,当我发送完设备地址后(读寄存器),此时给I2CTXD赋值,等我再发寄存器地址(此时主机在发SCL)时,从机就会和主机抢SDA线的控制权
页: [1] 2 3
查看完整版本: 第1篇_8H1K17 I2C从机如何/何时发送ACK/NACK