目录
2GD32F450最多支持3组硬件I2C,支持标速(100KHz)和快速(400KHz)。
1 GPIO初始化
以I2C0为例
//B6: SCL, B7: SDA
gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_6 | GPIO_PIN_7);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6 | GPIO_PIN_7);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
2 I2C初始化
2.1 RCU使能
rcu_periph_clock_enable(RCU_I2C0);
2.2 频率设置
对应的API函数
void i2c_clock_config(uint32_t i2c_periph, uint32_t clkspeed, uint32_t dutycyc)
clkspeed最大400KHz。dutycyc仅对Fast模式有效,有2个值:I2C_DTCY_2和I2C_DTCY_16_9,分别表示占空比2(L)/ 2(H)和16(L)/9(L)。
i2c_clock_config(i2cmGroup[port], (uint32_t)baud, I2C_DTCY_2);
2.3 地址模式设置
void i2c_mode_addr_config(uint32_t i2c_periph, uint32_t mode, uint32_t addformat, uint32_t addr)
mode: I2C_I2CMODE_ENABLE - I2C模式;I2C_SMBUSMODE_ENABLE - SMBUS模式
addformat:I2C_ADDFORMAT_7BITS - 7位地址模式,常用; I2C_ADDFORMAT_10BITS - 10位地址模式
addr:I2C地址
i2c_mode_addr_config(i2cmGroup[port], I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0);
2.4 使能I2C
i2c_enable(i2cmGroup[port]);
2.5 使能ACK
i2c_ack_config(i2cmGroup[port], I2C_ACK_ENABLE);
3 I2C读
实现读函数
bool_t i2cRead(uint8_t port, uint8_t slaveAddr, uint8_t addrbit, uint16_t addr, uint8_t *pdata, uint16_t len)
{
}
port:对应I2C的端口号,0-2
slaveAddr:I2C从设备地址
addrbit:设备内部地址宽度,有效值0,8,16
addr:设备内部地址
pdate:读入数据buffer指针
len:读入数据长度
注意,I2C初始化完后默认是使能ACK的。
3.1 等待I2C空闲
while(i2c_flag_get(i2cmGroup[port], I2C_FLAG_I2CBSY));
3.2 发送START起始位
i2c_start_on_bus(i2cmGroup[port]);
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_SBSEND));
使能I2C后I2C默认处于从设备的模式,发送一个START后,I2C硬件会设置SBSEND位然后进入主机模式。
3.3 发送slaveAddr
if(addrbit > 0)
{
i2c_master_addressing(i2cmGroup[port], slaveAddr, I2C_TRANSMITTER);
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_ADDSEND));
i2c_flag_clear(i2cmGroup[port],I2C_FLAG_ADDSEND);
while(SET != i2c_flag_get(i2cmGroup[port], I2C_FLAG_TBE));
...
}
I2C_TRANSMITTER表示接下来是写,如果从设备的内部地址大于0,则表示接下来是需要写入这个地址的。然后等待I2C硬件设置ADDSEND标志位为1,为1后软件清除这个标志位,最后等待I2C的发送数据缓冲为空(I2C_FLAG_TBE)。
3.4 发送addr
if(addrbit == 16)
{
i2c_data_transmit(i2cmGroup[port], (uint8_t)(addr >> 8));
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_BTC));
}
i2c_data_transmit(i2cmGroup[port], (uint8_t)(addr & 0xff));
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_BTC));
3.5 再发送一次START
i2c_start_on_bus(I2C0);
while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));
如果地址大于0才需要再发送一次START,以便再写入SlaveAddr开启读数据过程。
3.6 发送slaveAddr
i2c_master_addressing(i2cmGroup[port], slaveAddr, I2C_RECEIVER);
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_ADDSEND));
if(len == 1)
i2c_ack_config(i2cmGroup[port],I2C_ACK_DISABLE);
i2c_flag_clear(i2cmGroup[port],I2C_FLAG_ADDSEND);
这里I2C_RECEIVER表示接下来是读数据。
在清除I2C_FLAG_ADDSEND前还需要判断一下需要读多少数据,如果是最后一个字节,需要把ACK关掉,这样读完最后一个字节发送的是NACK了。
3.7 接收数据
while(len > 0)
{
if(i2c_flag_get(i2cmGroup[port], I2C_FLAG_RBNE))
{
if(len == 2)
i2c_ack_config(i2cmGroup[port],I2C_ACK_DISABLE);
*pdata = i2c_data_receive(i2cmGroup[port]);
pdata++;
len--;
}
}
当len等于2时,倒数第二个数据已经被写入DATA寄存器,ACK已经完成,在读DATA寄存器前将ACK设置为Disable,这样接下来读完DATA寄存器,I2C硬件会继续读入最后一个字节数据并发送NACK。
3.8 发送STOP
i2c_stop_on_bus(i2cmGroup[port]);
while(I2C_CTL0(i2cmGroup[port])&0x0200);
i2c_ack_config(i2cmGroup[port], I2C_ACK_ENABLE);
所有数据读完后,发送STOP,并等待结束,最后恢复ACK使能。
4 I2C写
流程与读类似,区别是只需要写一次salveAddr,也不需要处理ACK的使能。
bool_t i2cWrite(uint8_t port, uint8_t slaveAddr, uint8_t addrbit, uint16_t addr, uint8_t *pdata, uint16_t len)
{
if(port >= HW_I2C_MAX)
return FALSE;
while(i2c_flag_get(i2cmGroup[port], I2C_FLAG_I2CBSY));
i2c_start_on_bus(i2cmGroup[port]);
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_SBSEND));
i2c_master_addressing(i2cmGroup[port], slaveAddr, I2C_TRANSMITTER);
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_ADDSEND));
i2c_flag_clear(i2cmGroup[port],I2C_FLAG_ADDSEND);
while(SET != i2c_flag_get(i2cmGroup[port] , I2C_FLAG_TBE));
if(addrbit > 0)
{
if(addrbit == 16)
{
i2c_data_transmit(i2cmGroup[port], (uint8_t)(addr >> 8));
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_BTC));
}
i2c_data_transmit(i2cmGroup[port], (uint8_t)(addr & 0xff));
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_BTC));
}
while(len > 0)
{
i2c_data_transmit(i2cmGroup[port], *pdata);
while(!i2c_flag_get(i2cmGroup[port], I2C_FLAG_BTC));
pdata++;
len--;
}
i2c_stop_on_bus(I2C0);
while(I2C_CTL0(I2C0)&0x0200);
}