首先说明:
1.SMBus和I2C的区别
我曾尝试用江科大的I2C底层去直接读取该模块,但是无法成功,之后AI生成的的代码也无法成功。
思来想去最大的可能就是SMBus这个协议的问题,根据百度得到的结果如下:
SMBus和I2C的区别
链接:
smbus协议_百度百科https://baike.baidu.com/item/smbus%E5%8D%8F%E8%AE%AE/56360572.个人见解(希望有知道的可以补充说明或纠错):
①SMBus协议是I2C的pro版本,对时序要求更加严格。
②特别是在超时方面,I2C可能是在SCL的对应电平下改变SDA的电平,那就能正常操作,不在意时间多久;但SMBus则是要求了SCL在对应电平下,SDA应该在什么时候做出改变,否则超时。
代码:
以下是我用STM32F103C8T6能读取到我认为正常数据的代码,若不满足读者要求,请自行微调。
若对你有帮助,请点赞收藏~
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
//************** 宏定义 **************
#define I2C_SCL_PIN GPIO_Pin_6
#define I2C_SDA_PIN GPIO_Pin_7
#define I2C_GPIO_PORT GPIOB
#define Nack_counter 10
#define uint unsigned int
#define uchar unsigned char
//************** 全局变量 **************
volatile uchar DataH, DataL, Pecreg;
volatile uint temp;
//************** 函数声明 **************
void I2C_GPIO_Init(void);
void start_bit(void);
void stop_bit(void);
void send_bit(uchar bit_val);
uchar rx_byte(void);
uchar tx_byte(uchar dat_byte);
void delay_us(uint us);
uint memread(void);
//************** GPIO初始化 **************
void I2C_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟
// SCL和SDA配置为开漏输出
GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);
// 初始状态拉高
GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN | I2C_SDA_PIN);
}
//************** 延时函数(72MHz主频)**************
void delay_us(uint us) {
us *= 72/5; // 72MHz下1us需要72个周期(实测需校准)
while(us--) {
__NOP();
}
}
//************** I2C起始信号 **************
void start_bit(void) {
GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
delay_us(5); // 保持时间≥4.7us
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
delay_us(5);
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
}
//************** I2C停止信号 **************
void stop_bit(void) {
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
delay_us(5);
GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
delay_us(5);
GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
}
//************** 发送单bit **************
void send_bit(uchar bit_val) {
if(bit_val) {
GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
} else {
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
}
delay_us(2);
GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
delay_us(5); // SCL高电平≥4.0us
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
delay_us(2);
}
//************** 接收单bit **************
uchar receive_bit(void) {
uchar bit_val;
GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN); // 释放SDA线
delay_us(2);
GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
delay_us(5);
bit_val = GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_PIN);
GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
delay_us(2);
return bit_val;
}
//************** 发送单字节 **************
//void tx_byte(uchar dat_byte) {
// uchar i, n = Nack_counter;
//
//TX_again:
// for(i = 0; i < 8; i++) {
// send_bit((dat_byte & 0x80) ? 1 : 0);
// dat_byte <<= 1;
// }
//
// if(receive_bit()) { // 检测NACK
// stop_bit();
// if(n--) {
// start_bit();
// goto TX_again;
// }
// }
//}
uchar tx_byte(uchar dat_byte)
{
for(uchar i = 0; i < 8; i++) {
send_bit((dat_byte & 0x80) ? 1 : 0);
dat_byte <<= 1;
}
return !receive_bit(); // 返回1=ACK, 0=NACK
}
//************** 接收单字节 **************
uchar rx_byte(void) {
uchar i, dat = 0;
for(i = 0; i < 8; i++) {
dat <<= 1;
if(receive_bit()) dat |= 0x01;
}
send_bit(0); // 发送ACK
return dat;
}
//************** 读取温度数据 **************
//uint memread(void) {
// start_bit();
// tx_byte(0x00); // MLX90614地址+写
// tx_byte(0x07); // 读取RAM命令
//
// start_bit();
// tx_byte(0xB5); // MLX90614地址+读
//
// DataL = rx_byte();
// DataH = rx_byte();
// Pecreg = rx_byte();
//
// stop_bit();
// return (DataH << 8) | DataL;
//}
uint memread(void) {
start_bit();
if (!tx_byte(0xB4)) { // 发送地址+写
stop_bit();
return 0xFFFF;
}
if (!tx_byte(0x07)) { // 发送命令(读取RAM)
stop_bit();
return 0xFFFF;
}
start_bit();
if (!tx_byte(0xB5)) { // 发送地址+读
stop_bit();
return 0xFFFF;
}
DataL = rx_byte(); // 低字节
DataH = rx_byte(); // 高字节
Pecreg = rx_byte(); // PEC(可选)
stop_bit();
return ((DataH << 8) | DataL)*0.02 - 273.15;
}
//************** 主函数 **************
int main(void) {
SystemInit(); // 系统时钟配置为72MHz
I2C_GPIO_Init();
while(1) {
temp = memread();
// 此处添加温度处理代码
delay_us(20000); // 延时20ms
}
}
使用方法:
①直接copy到自己的main.c
②下载到STM32F103C8T6板子上,并保持连接。根据下面代码接线,供电3.3V
#define I2C_SCL_PIN GPIO_Pin_6
#define I2C_SDA_PIN GPIO_Pin_7
#define I2C_GPIO_PORT GPIOB
③使用debug模式查看数据,具体如下:


若点击运行后temp数据不变,则需要进行下面这一步:
