一、开发环境
开发软件:STVD
芯片型号:stm8s103
硬件接口:I2C(主模式)
二、寄存器主要用到的功能(按用户手册的顺序)
1. I2C_CR1(控制寄存器1):
(1)bit0:控制I2C模块的启动/禁用。此功能主要用于I2C总线初始化的时候,配置TRISER相关寄存器功能的时候必须先禁用I2C模块才可以进行写操作。
2. I2C_CR2(控制寄存器2)
(1)bit7:设置1实现软复位I2C模块。当I2C总线始终处于繁忙状态的时候通过复位实现恢复。
(2)bit3:设置ACK应答的位置。在I2C总线初始化的时候配置,通常是作清零操作,用于表示ACK位控制被移位寄存器正在接收的这个当前字节的应答或者不应答。
(3)bit2:设置ACK应答使能。这个位需要根据协议决定是否需要使能ACK,并在I2C主机每读取来自从机的一字节数据之前进行设置,这样硬件II2C就会自动在读取到数据后作出对应的ACK响应。实际上这个ACK应答是始终存在的,如果该位为0,表示当前应答位为NACK,许多I2C设备在读取最后一个字节的数据的时候会要求主机回复的是NACK,其实就是把该位设置为0即可。
(4)bit1:设置该位用于产生停止位。用于释放总线,也就是总线恢复闲置状态。
(5)bit0:设置该位用于产生起始位。产生起始位后,进入主模式。
3. I2C_FREQR(频率寄存器)
(1)bit5~0:设置外设时钟的频率。有效的取值范围0b000001~0b110010表示1MHz~50MHz。在I2C总线初始化的时候配置;如果用内部晶振,数值设置为16即可表示外设时钟的频率是16MHz;如果用的是外部晶振,根据外部晶振的时钟频率设置对应的数值即可。
4. I2C_OARL(自身地址寄存器LSB)
(1)bit7~1:设置主机在I2C总线上自身的地址(地址的第7:1位)。
(2)bit0:设置主机在I2C总线上的自身地址(地址的第0位,在地址位数为10的时候有效)
5. I2C_OARH(自身地址寄存器MSB)
(1)bit2~1:设置主机在I2C总线上的自身地址(地址的第9:8位,在地址位数为10的时候有效)
6. I2C_DR(数据寄存器)
(1)bit7~0:用于存放接收到的数据或放置用于发送到总线的数据。这个寄存器的用法很简单,主机需要往从机写数据的时候把数据按字节写入此寄存器即可,主机需要往从机读数据的时候把这个寄存器内的数据读取并写入变量中即可。
7. I2C_SR1(状态寄存器1)
(1)bit7:通过判断该位可以知道写入I2C_DR的数据是否已经全部发送出去,从而决定是否开始发送下一个字节的数据。
(2)bit6:通过判断该位可以知道是否接受到来自从机的数据,该位被置位的状态下,从I2C_DR中读取数据才是有意义的。
(3)bit1:通过判断该位可以知道需要与主机进行通讯的从机地址是否已经发送。
(4)bit0:通过判断该位可以知道起始位是否已经发送。
8. I2C_SR2(状态寄存器2):目前开发过程中没有用到该寄存器。
9. I2C_SR3(状态寄存器3):目前开发过程中没有用到该寄存器。
10. I2C_ITR(中断寄存器):如果开发时希望特定事件以中断形式通知,可以通过该寄存器来实现。比如,写入一字节数据至I2C_DR寄存器后,CPU处理其它事件,当收到TXE触发的中断事件后,再写下一字节数据。这样CPU在处理整个I2C读写事件的过程中可以不需要进行循环判断等待。
11. I2C_CCRL(时钟控制寄存器低位部分),I2C_CCRH(时钟控制寄存器高位部分)
(1)这两个寄存器在I2C总线初始化的时候进行配置,需要注意的是这两个寄存器需要在I2C模块禁用的状态下改变值。
(2)I2C_CCRH的bit7:如果I2C通讯速度小于等于标准速度(100KHz),该位置0;如果I2C通讯速度大于标准速度,则该位置1表示是快速模式(比较常用的是400kHz)
(3)I2C_CCRL的bit6:表示快速模式下的占空比,即I2C总线的SCL时钟信号的低电平时间(t_low)与高电平时间(t_high)的比值。t_low+t_high就是一个SCL时钟信号脉冲周期的长度,i2c总线的通讯速度f_scl = 1/(t_low+t_high)。假如单片机采用16MHz的外部晶振且I2C通讯速度是400KHz的情况下,
(4)I2C_CCRH的bit3:0以及I2C_CCRL的bit7~0共12个bit表示CCR的值。CCR的具体计算方式举个例子大致描述一下:假如单片机采用16MHz的外部晶振(即t_ck=16)且I2C通讯速度是400KHz的情况下,此时采用占空比为16:9(I2C_CCRL的bit6设置为1),则CCR=(t_ck)/[1/(t_low+t_high)*(25)]=(16*1000000)/(400000*25)= 1
12. I2C_TRISE(TRISE寄存器)
(1)bit5~0:在快速/标准模式下的大上升时间(主模式)。这个时间需要根据从机的特性需求来设置。
标准模式(100KHz)下的计算公式:(1000ns / t_pclk1 + 1) ns; t_pclk1为从机的最大上升时间
快速模式(400KHz)下的计算公式:(300ns / t_pclk1 + 1)ns
三、调试心得
1. 如果外设时钟的频率小于10MHz,建议在标准模式(100KHz通讯速度)下进行开发调试。
2. 发送从机地址时(7-bit),需要加上1bit读写位(0为写,1为读),拼成一整个字节进行发送。
3. 发送完地址+读写字节后,必须对I2C_SR3寄存器进行读操作,实现清状态寄存器,否则总线会堵死。
4. 读取数据前需设置ACK应答,如果需要NACK则设置ACK应答位为0(示例函数中ACK设置为0,需根据协议需求进行修改)
四、读写函数代码
1. 主机往从机读一个字节数据:
#define I2C_OVER_TIME 0xFA // 超时处理时间需要根据晶振频率调整
#define SLAVE_ADDR_WR 0x20 // 7-bit从机地址 + 1-bit写信号
#define SLAVE_ADDR_RD 0x21 // 7-bit从机地址 + 1-bit读信号
u8 I2C_Read_Byte(u8 reg, u8 *pdata)
{
u16 cnt = 0;
/* Generate start & wait event detection */
I2C->CR2 |= 0x01;
while (!(I2C->SR1 & 0x01))
{
if ( ++cnt > I2C_OVER_TIME )
{
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 1;
}
}
cnt = 0;
/* Send slave Address in write direction & wait detection event */
I2C->DR = SLAVE_ADDR_WR;
while (!(I2C->SR1 & 0x02))
{
if ( ++cnt > I2C_OVER_TIME )
{
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 1;
}
}
I2C->SR3;
cnt = 0;
/* Send register to be read */
I2C->DR = reg;
while(!(I2C->SR1 & 0x80))
{
if ( ++cnt > I2C_OVER_TIME )
{
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 1;
}
}
cnt = 0;
/* Generate start & wait event detection */
I2C->CR2 |= 0x01;
while (!(I2C->SR1 & 0x01))
{
if ( ++cnt > I2C_OVER_TIME )
{
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 1;
}
}
cnt = 0;
/* Send register to be read */
I2C->DR = SLAVE_ADDR_RD;
while(!(I2C->SR1 & 0x02))
{
if ( ++cnt > I2C_OVER_TIME )
{
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 1;
}
}
I2C->SR3;
cnt = 0;
I2C->CR2 &= ~0x04; //nack
/* waiting for byte from slave */
while (!(I2C->SR1 & 0x40))
{
if ( ++cnt > I2C_OVER_TIME )
{
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 1;
}
}
*pdata = (u8)(I2C_ReceiveData());
/* Generate stop signal */
I2C->CR2 |= 0x02;
return 0;
}
2. 主机往从机写一字节数据:
u8 I2C_Write_Byte(u8 reg,u8 data)
{
u16 cnt = 0;
/* Generate start & wait event detection */
I2C->CR2 |= 0x01;
while (!(I2C->SR1 & 0x01))
{
if ( ++cnt > I2C_OVER_TIME )
{
I2C_GenerateSTOP(ENABLE);
return 1;
}
}
cnt = 0;
/* Send slave Address in WRITE direction & wait detection event */
I2C->DR = SLAVE_ADDR_WR;
while (!(I2C->SR1 & 0x02))
{
if ( ++cnt > I2C_OVER_TIME )
{
I2C_GenerateSTOP(ENABLE);
return 1;
}
}
I2C->SR3;
cnt = 0;
/* Send internal register address to write */
I2C->DR = reg;
while(!(I2C->SR1 & 0x80))
{
if ( ++cnt > I2C_OVER_TIME )
{
I2C_GenerateSTOP(ENABLE);
return 1;
}
}
cnt = 0;
/* Send Data to write */
I2C->DR = data;
while (!(I2C->SR1 & 0x80))
{
if ( ++cnt > I2C_OVER_TIME )
{
I2C_GenerateSTOP(ENABLE);
return 1;
}
}
I2C_GenerateSTOP(ENABLE);
return 0;
}
上一篇:stm8 IAR 编译错误Fatal Error[Cp001]
下一篇:STM8L051的硬件I2C调试
推荐阅读最新更新时间:2024-03-16 16:26