ercircle
发表于 2025-3-18 18:47:31
zhouq 发表于 2025-3-18 18:21
按照你数据手册从机中断的代码例程,当我发送完设备地址后(读寄存器),此时给I2CTXD赋值,等我再发寄存器 ...
赋值完这个时候按我理解SDA线控制权应该在从机,从机回数据用呀。你上传下你测试用的主从机代码和电路图么
zhouq
发表于 2025-3-18 18:50:52
ercircle 发表于 2025-3-18 18:47
赋值完这个时候按我理解SDA线控制权应该在从机,从机回数据用呀。你上传下你测试用的主从机代码和电路图 ...
I2C规范中,当主机读从机数据时,SDA控制权需要转交给从机。回帖中有我写寄存器的代码截图
zhouq
发表于 2025-3-18 18:53:16
ercircle 发表于 2025-3-18 18:11
图里不是收到主机事件,将数据放到寄存器了嘛,放了SDA引脚应该就有反应了。
“图里不是收到主机事件,将数据放到寄存器了嘛,放了SDA引脚应该就有反应了。”
“放了SDA引脚应该就有反应了”我猜测也是这样,数据放到了SDA后,主机再发时钟信号,此时STC从机就会在SDA引脚动作。
ercircle
发表于 2025-3-18 19:41:14
zhouq 发表于 2025-3-18 17:45
主机发送完设备地址,寄存器地址,之后再发送起始信号,然后呢?从机通过什么方式把数据放到SDA引脚上?
...
只有这一张截图吗?操作SCL是主机代码啊,你的从机代码呢?
“等我再发寄存器地址(此时主机在发SCL)时,从机就会和主机抢SDA线的控制权”
这句话又是什么意思呢,从机发什么寄存器地址,从机直接发数据么,主机此时发SCL和SDA线收数据同时进行。
神农鼎
发表于 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);
}
zhouq
发表于 2025-3-19 09:46:31
ercircle 发表于 2025-3-18 19:41
只有这一张截图吗?操作SCL是主机代码啊,你的从机代码呢?
“等我再发寄存器地址(此时主机在发SCL)时 ...
这是我的从机代码
zhouq
发表于 2025-3-19 09:47:24
这是我的从机代码,大家看下有无问题
/* Includes ------------------------------------------------------------------*/
#include "HeaderInc.h"
/* Private define-------------------------------------------------------------*/
#define User_I2C_ADDR0x30 //从机地址为0x30,高7位,MA=0,MA=0表示设备地址必须与I2CSLADR相同
#define WRMask 0x01 //读写操作掩码
#define ADDR_IAP_17A 0x8A //IAP升级芯片地址
//I2C配置寄存器(I2CCFG)
//使能I2C从机模式
//B7-B6 10
#define EnI2C_Slave 0x80
//使能I2C主机模式
//B7-B6 11
//#define EnI2C_Master 0xC0
//MSSPEED,I2C在主机模式下,MSSPEED设置才有效
//I2C总线速度=FOSC / 2 / (MSSPEED * 2 + 4),设置总线速度为400K,那么MSSPEED=13
//#define I2C_Speed 0x0D
//I2C 从机状态寄存器(I2CSLST)
#define I2C_S_BUSY 0x80 //从机状态位,0空闲,1忙,只读
#define I2C_S_STAIF 0x40 //收到START信号的中断请求位
#define I2C_S_RXIF 0x20 //从机收到1字节的数据的中断请求位
#define I2C_S_TXIF 0x10 //从机发完1字节的数据的中断请求位
#define I2C_S_STOIF 0x08 //从机收到STOP信号的中断请求位
#define I2C_S_ACKI 0x02 //从机接收到的ACK数据
#define I2C_S_ACKO 0x01 //从机准备要发送的ACK信号
/* Private variables----------------------------------------------------------*/
/* Private function prototypes------------------------------------------------*/
//声明使用的函数
static void I2C_S_Init(void); //I2C初始化
/* Public variables-----------------------------------------------------------*/
//创建并初始化结构体
I2C_S_t idata I2C_S =
{
FALSE, /* DevAddrFlag - 初始化时未接收到设备地址,即未收到任何数据 */
FALSE, /* RegAddrFlag - 初始化时未接收到寄存器地址 */
FALSE, /* 读写标志位,TRUE 表示读操作,FALSE 表示写操作 */
0, /* DevAddr */
0, /* RegAddr */
I2C_S_Init/* Init */
};
/*
* @name I2C_S_Init
* @briefI2C初始化
* @paramNone
* @retval None
*/
static void I2C_S_Init(void)
{
//设置P14 P15为 I2C 功能
P_SW2 |= 0x80;
//设置从机设备地址寄存器I2CSLADR=0011_0000B
//B0为MA,设置为0表示设备地址必须与I2CSLADR相同
I2CSLADR = User_I2C_ADDR;
//配置从机状态为空闲,并清零所有中断标志
I2CSLST = 0x00;
//使能I2C从机模式,此寄存器涉及到端口
I2CCFG = EnI2C_Slave;
}
/*
* @name I2C_INTR()
* @briefI2C中断函数
* @param参数说明
* @retval None
*/
void I2C_INTR() interrupt 24
{
//收到START信号
if (I2CSLST & I2C_S_STAIF)
{
I2CSLST &= ~I2C_S_STAIF;//清零STAIF标志位
}//从机收到了1字节数据
else if (I2CSLST & I2C_S_RXIF)
{
I2CSLST &= ~I2C_S_RXIF;//清零RXIF标志位
//接收设备地址
if (I2C_S.DevAddrFlag == FALSE)//如果设备标志位为FALSE,表示这是收到的第1字节
{
I2C_S.DevAddr = I2CRXD;//接收设备地址
//下面代码多余了,MA=0时设备地址只有和I2CSLADR相同才会处理数据,留着也无妨
//第1字节数据是设备地址数据
if((I2C_S.DevAddr & 0xFE) == User_I2C_ADDR)
{
I2C_S.DevAddrFlag = TRUE;//处理RECV事件(RECV DEVICE ADDR)
if (I2C_S.DevAddr & WRMask) {
I2C_S.ReadWriteFlag = TRUE;// 读操作
} else {
I2C_S.ReadWriteFlag = FALSE;// 写操作
}
}
}//接收寄存器地址,已经收到过设备地址,且I2C_S.RegAddrFlag为 FALSE,则表示此次数据是寄存器地址
else if ((I2C_S.DevAddrFlag == TRUE) && (I2C_S.RegAddrFlag == FALSE))
{
I2C_S.RegAddrFlag = TRUE;//处理RECV事件(RECV REG ADDR)
I2C_S.RegAddr = I2CRXD;//接收寄存器地址
}//读寄存器
else if(I2C_S.ReadWriteFlag == TRUE)
{
//当前只读四个通道的值
if (I2C_S.RegAddr == ChA_ID ||
I2C_S.RegAddr == ChB_ID ||
I2C_S.RegAddr == ChC_ID ||
I2C_S.RegAddr == ChD_ID)
{
ChxDetec.BitValue = ChxDetec.GetBitValue(I2C_S.RegAddr);
I2CTXD = (uint8_t)(ChxDetec.BitValue >> 8);//上传低位的ADC值
}
else if(I2C_S.RegAddr == ADDR_IAP_17A)
{
}
}//写寄存器
else if(I2C_S.ReadWriteFlag == FALSE)
{
if(I2C_S.RegAddr == ADDR_IAP_17A)
{
//buffer = I2CRXD;
}
}
}//从机发送完了1字节数据
else if (I2CSLST & I2C_S_TXIF)
{
I2CSLST &= ~I2C_S_TXIF;//清零TXIF标志位
if(I2CSLST & I2C_S_ACKI)//如果ACKI=1,说明主机NACK了
{
//主机NACK停止发送数据
}//接收到ACK
else
{
if (I2C_S.RegAddr == ChA_ID ||
I2C_S.RegAddr == ChB_ID ||
I2C_S.RegAddr == ChC_ID ||
I2C_S.RegAddr == ChD_ID)
{
ChxDetec.BitValue = ChxDetec.GetBitValue(I2C_S.RegAddr);
I2CTXD = (uint8_t)(ChxDetec.BitValue & 0x00FF);//上传低位的ADC值
}
else if(I2C_S.RegAddr == ADDR_IAP_17A)
{
//I2CTXD = 0;
}
}
}//从机收到STOP信号
else if (I2CSLST & I2C_S_STOIF)
{
I2CSLST &= ~I2C_S_STOIF;//清零STOIF标志位
I2C_S.DevAddrFlag = FALSE;//将设备地址标志位恢复默认值
I2C_S.RegAddrFlag = FALSE;//将寄存器地址标志位恢复默认值
I2C_S.ReadWriteFlag = FALSE;
I2C_S.DevAddr = 0;
I2C_S.RegAddr = 0;
}
}
ercircle
发表于 2025-3-19 12:02:47
zhouq 发表于 2025-3-19 09:47
这是我的从机代码,大家看下有无问题
还是不完整,用的中断模式,没见开I2CSLCR中断。
你用例程能跑通吗?
另外sda,scl用逻辑分析仪抓包没有,什么现象呢?
zhouq
发表于 2025-3-19 13:40:00
ercircle 发表于 2025-3-19 12:02
还是不完整,用的中断模式,没见开I2CSLCR中断。
你用例程能跑通吗?
另外sda,scl用逻辑分析仪抓包没有 ...
在别的文件配置了:
//I2C从机模式的各种中断使能
I2CSLCR = 0x78;
还没开始验证,我猜测芯片例程应该没问题,I2CRXT被赋值后,接收到SCL时钟信号就会把数据发出去。我在STC8G1K17A上成功实现过I2C功能,但只有设备地址,没有寄存器地址的读写
ercircle
发表于 2025-3-19 13:46:37
zhouq 发表于 2025-3-19 13:40
在别的文件配置了:
//I2C从机模式的各种中断使能
I2CSLCR = 0x78;
注意I2CRXD 是收,I2CTXD才是发送寄存器。