新手入门
积分 8
金钱 8
注册时间 2019-11-10
在线时间 1 小时
2 金钱
同样的程序,我今天上午可以实现读取的数据是正确的,然后我把读取给注释掉了,再次写入,然后断电,注释掉写入程序,恢复了读取,结果oled屏就完全不显示东西了。后来我把程序恢复原样,也没有反应了。我想问一下是我上述操作有问题吗,或者猜测一下可能是哪里出现了问题呢
单步执行了几次,它有时会卡在启动传输后的i2c_Wait,有时卡在写地址后面的这个i2c_Wait
我的单片机用的是k60,eeprom用的是24c02
不知道哪里错了,新手小白,求大佬指教
以下是我的程序:
谢谢~
#include "common.h"
#include "include.h"
#include "port.h"
#include "i2c.h"
#define AT24C02_i2c_ADDRESS 0x50
unsigned char MasterTransmission;
unsigned char SlaveID;
I2C_MemMapPtr I2CN[2] = {I2C0_BASE_PTR, I2C1_BASE_PTR}; //定义两个指针数组保存 I2CN 的地址
/*
* 把I2C通信的每个小步骤都用宏定义来实现,方便编写顶层函数
* 此宏定义参考飞思卡尔公司例程修改所得
*/
#define i2c_DisableAck(I2Cn) I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_TXAK_MASK
//
#define i2c_RepeatedStart(I2Cn) I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_RSTA_MASK
//启动信号
#define i2c_Start(I2Cn) I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_TX_MASK;\
I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_MST_MASK
//暂停信号
#define i2c_Stop(I2Cn) I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_MST_MASK;\
I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TX_MASK
//进入接收模式(应答)
#define i2c_EnterRxMode(I2Cn) I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TX_MASK;\
I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TXAK_MASK
//进入接收模式(不应答)
#define i2c_PutinRxMode(I2Cn) I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TX_MASK
//等待 I2C0_S
#define i2c_Wait(I2Cn) while(( I2C_S_REG(I2CN[I2Cn]) & I2C_S_IICIF_MASK)==0) {} \
I2C_S_REG(I2CN[I2Cn]) |= I2C_S_IICIF_MASK;
//写一个字节
#define i2c_write_byte(I2Cn,data) I2C_D_REG(I2CN[I2Cn]) = data
/*!
* @brief I2C初始化,设置波特率
* @param I2Cn_e I2C模块(I2C0、I2C1)
* @param baud 期待的波特率
* @return 实际的波特率
* @since v5.0
* Sample usage: i2c_init(I2C0,400*1000); // 初始化I2C0,期待的波特率为400k
*/
uint32 i2c_init(I2Cn_e i2cn, uint32 baud)
{
if(i2cn == I2C0)
{
/* 开启时钟 */
#if defined(MK60DZ10)
SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK; //开启 I2C0时钟
#elif defined( MK60F15)
SIM_SCGC4 |= SIM_SCGC4_IIC0_MASK; //开启 I2C0时钟
#endif
/* 配置 I2C0功能的 GPIO 接口 */
if((I2C0_SCL_PIN == PTB0) || (I2C0_SCL_PIN == PTB2) || (I2C0_SCL_PIN == PTD8) )
{
PORT_FuncInit (I2C0_SCL_PIN, ALT2 | ODO | PULLUP );
}
else
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
if((I2C0_SDA_PIN == PTB1) || (I2C0_SDA_PIN == PTB3) || (I2C0_SDA_PIN == PTD9) )
PORT_FuncInit (I2C0_SDA_PIN, ALT2 | ODO | PULLUP );
else
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
else
{
/* 开启时钟 */
#if defined(MK60DZ10)
SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK; //开启 I2C1时钟
#elif defined(MK60F15)
SIM_SCGC4 |= SIM_SCGC4_IIC1_MASK; //开启 I2C1时钟
#endif
/* 配置 I2C1功能的 GPIO 接口 */
if(I2C1_SCL_PIN == PTE1)
PORT_FuncInit (I2C1_SCL_PIN, ALT6 | ODO | PULLUP );
else if(I2C1_SCL_PIN == PTC10)
PORT_FuncInit (I2C1_SCL_PIN, ALT2 | ODO | PULLUP );
else
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
if(I2C1_SDA_PIN == PTE0)
PORT_FuncInit (I2C1_SDA_PIN, ALT6 | ODO | PULLUP );
else if (I2C1_SDA_PIN == PTC11)
PORT_FuncInit (I2C1_SDA_PIN, ALT2 | ODO | PULLUP );
else
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
/* 设置频率 */
// I2C baud rate = bus speed (Hz)/(mul × SCL divider) 即这里 50MHz/(1 ×128)=390.625kHz
// SDA hold time = bus period (s) × mul × SDA hold value
// SCL start hold time = bus period (s) × mul × SCL start hold value
// SCL stop hold time = bus period (s) × mul × SCL stop hold value
//查表 ICR 对应的 SCL_divider ,见 《K60P144M100SF2RM.pdf》第1468页的 I2C Divider and Hold Values
uint16 ICR_2_SCL_divider[0x40] =
{
20, 22, 24, 26, 28, 30, 34, 40, 28, 32, 36, 40, 44, 48, 56, 68,
48, 56, 64, 72, 80, 88, 104, 128, 80, 96, 112, 128, 144, 160, 192, 240,
160, 192, 224, 256, 288, 320, 384, 480, 320, 384, 448, 512, 576, 640, 768, 960,
640, 768, 896, 1024, 1152, 1280, 1536, 1920, 1280, 1536, 1792, 2048, 2304, 2560, 3072, 3840
};
uint8 mult;
if(bus_clk_khz <= 50000)mult = 0; //bus 一分频
else if(bus_clk_khz <= 100000)mult = 1; //bus 二分频
else mult = 2; //bus 四分频
uint16 scldiv = bus_clk_khz * 1000 / ( (1<<mult) * baud ); //最佳的分频系数
//需要从 ICR_2_SCL_divider 里找到 与最佳分频系数scldiv最相近的 分频系数
uint8 icr, n = 0x40;
uint16 min_Dvalue = ~0, Dvalue;
while(n) //循环里逐个扫描,找出最接近的 分频系数
{
n--;
Dvalue = abs(scldiv - ICR_2_SCL_divider[n]);
if(Dvalue == 0)
{
icr = n;
break; //退出while循环
}
if(Dvalue < min_Dvalue)
{
icr = n;
min_Dvalue = Dvalue;
}
}
I2C_F_REG(I2CN[i2cn]) = ( 0 // I2C Frequency Divider register (I2CN_F) I2C分频寄存器 I2C最大波特率为 400k
| I2C_F_MULT(mult) // 乘数因子 mul = 1<<MULT
| I2C_F_ICR(icr) // 时钟速率 = ICR_2_SCL_divider[ICR] ,查表获得 ICR 与 SCL_divider 映射关系
);
/* 使能 I2C */
I2C_C1_REG(I2CN[i2cn]) = ( 0
| I2C_C1_IICEN_MASK //使能I2C
//| I2C_C1_IICIE_MASK //使能中断
);
return ( bus_clk_khz * 1000 / ( (1<<mult) * ICR_2_SCL_divider[icr]) );
}
/*!
* @brief I2C通信结束后需要调用的函数函数
* @since v5.0
* @note 如果通信失败,可尝试增大此延时值,确认是否延时导致的
*/
void Pause(void)
{
u16 n;
for(n = 1; n < 50000; n++) //注意,这个数据太小,会导致读取错误。
{
asm("nop");
}
}
void i2c_StartTransmission (I2Cn_e i2cn, u8 SlaveID, MSmode Mode)
{
//ASSERT(Mode == MWSR || Mode == MRSW); //使用断言,检测 Mode 是否为 读 或 写
SlaveID = ( SlaveID << 1 ) | Mode ; //确定写地址和读地址
/* send start signal */
i2c_Start(i2cn);
/* send ID with W/R bit */
i2c_write_byte(i2cn, SlaveID);
}
/*!
* @brief 读取I2C设备指定地址寄存器的数据
* @param I2Cn_e I2C模块(I2C0、I2C1)
* @param SlaveID 从机地址(7位地址)
* @param reg 从机寄存器地址
* @return 读取的寄存器值
* @since v5.0
* Sample usage: uint8 value = i2c_read_reg(I2C0, 0x1D, 1);
*/
u8 i2c_read_reg(I2Cn_e i2cn, u8 SlaveID, u8 Addr)
{
u8 result;
/* Send Slave Address */
i2c_StartTransmission (i2cn, SlaveID, MWSR);
i2c_Wait(i2cn);
/* Write Register Address */
i2c_write_byte(i2cn, Addr);
i2c_Wait(i2cn);
/* Do a repeated start */
i2c_RepeatedStart(i2cn);
/* Send Slave Address */
i2c_write_byte(i2cn, ( SlaveID << 1) | MRSW );
i2c_Wait(i2cn);
/* Put in Rx Mode */
i2c_PutinRxMode(i2cn);
/* Turn off ACK since this is second to last byte being read*/
i2c_DisableAck(i2cn); //不应答
/* Dummy read 虚假读取*/
result = I2C_D_REG(I2CN[i2cn]);
i2c_Wait(i2cn);
/* Send stop since about to read last byte */
i2c_Stop(i2cn);
/* Read byte */
result = I2C_D_REG(I2CN[i2cn]);
return result;
}
/*!
* @brief 写入一个字节数据到I2C设备指定寄存器地址
* @param I2Cn_e I2C模块(I2C0、I2C1)
* @param SlaveID 从机地址(7位地址)
* @param reg 从机寄存器地址
* @param Data 数据
* @since v5.0
* Sample usage: i2c_write_reg(I2C0, 0x1D, 1,2); //向从机0x1D 的寄存器 1 写入数据 2
*/
void i2c_write_reg(I2Cn_e i2cn, u8 SlaveID, u8 Addr, u8 Data)
{
/* send data to slave */
i2c_StartTransmission(i2cn, SlaveID, MWSR); //启动传输
i2c_Wait(i2cn);
i2c_write_byte(i2cn, Addr); //写地址
i2c_Wait(i2cn);
i2c_write_byte(i2cn, Data); //写数据
i2c_Wait(i2cn);
i2c_Stop(i2cn);
Pause(); //延时太短的话,可能写出错
}
char txt[20];
int b=3;
int main()
{
#define ADDR 0x00
u8 i = 0;
u8 Data=0;
UART_Init(UART0,9600); //初始化串口
LCD_Init(); //显示屏
i2c_init(I2C0,400 * 1000); //初始化i2c0
while(1)
{
for(i=0;i<2;i++)
{
i2c_write_reg(I2C0, AT24C02_i2c_ADDRESS, ADDR, 1); //i2c向AT24C02_i2c_ADDRESS芯片写入数据 i 到地址为ADDR的寄存器
DelayMs(50);
Data = i2c_read_reg(I2C0,AT24C02_i2c_ADDRESS, ADDR);
DelayMs(50);
printf("接收到的数据为:%d\n\n", Data); //蓝牙发送数据
sprintf(txt,"A=%d",Data);
LCD_P6x8Str(0,0,(uint8*)txt); //oled屏显示数据
}
}
}
我来回答