项目中遇到MCU通过I2C访问从机寄存器,对其进行读写操作的任务时,可以使用GPIO软件模拟I2C和硬件I2C两种方式进行。下面介绍软件模拟I2C的驱动代码。
一、创建a_sw_i2c.c源文件用于编辑软件模拟i2c通信协议和读\写操作函数
(1)GPIO端口配置。
#include "a_sw_i2c.h"
void a_i2c_gpio_config(void)
{
// enable GPIO clock
rcu_periph_clock_enable(RCU_GPIOB);
// connect PB6 to I2C0_SCL connect PB7 to I2C0_SDA
gpio_init(GPIOB, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
gpio_init(GPIOB, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
}
(2)起始信号、停止信号、应答信号、不应答信号、应答信号检测。
/* start signal */
void a_sw_i2c_start(void)
{
SCL_HIGH;
SDA_HIGH;
a_delay_5us;
SDA_LOW;
a_delay_5us;
SCL_LOW;
a_delay_5us;
}
/* stop signal */
void a_sw_i2c_stop(void)
{
SDA_LOW;
SCL_HIGH;
a_delay_5us;
SDA_HIGH;
}
/* ack signal */
void a_ack(void)
{
SDA_LOW;
a_delay_5us;
SCL_HIGH;
a_delay_5us;
SCL_LOW;
a_delay_5us;
SDA_HIGH;
}
/* nack signal */
void a_nack(void)
{
SDA_HIGH;
a_delay_5us;
SCL_HIGH;
a_delay_5us;
SCL_LOW;
a_delay_5us;
}
/* wait ack signal */
uint8_t a_wait_ack(void)
{
AW_U32 ack_state;
SDA_HIGH;
a_delay_5us;
SCL_HIGH;
a_delay_5us;
// read sda state:1 nack,0 ack
if (SDA_READ) {
ack_state = 1;
} else {
ack_state = 0;
}
SCL_LOW;
a_delay_5us;
return ack_state;
}
(3)总线数据接收与发送。
/* send 8bit to i2c bus */
void aw_send_byte(uint8_t byte)
{
uint8_t i;
for (i = 0; i < 8; i++) {
if (byte & HIGH_BIT) {
SDA_HIGH;
} else {
SDA_LOW;
}
a_delay_5us;
SCL_HIGH;
a_delay_5us;
SCL_LOW;
// send complete release bus
if (i == 7) {
SDA_HIGH;
}
byte <<= 1;
a_delay_5us;
}
}
/* receive 8bit from i2c bus */
uint8_t a_receive_byte(void)
{
uint8_t i;
uint8_t value = 0;
for (i = 0; i < 8; i++) {
value <<= 1;
SCL_HIGH;
a_delay_5us;
if (SDA_READ) {
value |= 1;
}
SCL_LOW;
a_delay_5us;
}
return value;
}
(4)单字节读\写接口函数。
/* read one byte data*/
uint8_t a_sw_i2c_read_byte(uint8_t slave_addr, uint8_t register_addr, uint8_t data)
{
// send start signal
a_sw_i2c_start();
// send slave address write
a_send_byte(slave_addr << LEFT_SHIFT);
// wait ack
a_wait_ack();
// slave register address
a_send_byte(register_addr);
// wait ack
a_wait_ack();
// send start signal
a_sw_i2c_start();
// send slave address read
a_send_byte(slave_addr << LEFT_SHIFT + READ_BIT);
// wait ack
a_wait_ack();
// read one byte data
data = a_receive_byte();
// read completed send nack signal
a_nack();
// send stop signal
a_sw_i2c_stop();
return data;
}
/* write one byte data*/
uint8_t a_sw_i2c_write_byte(uint8_t slave_addr, uint8_t register_addr, uint8_t data)
{
// send start signal
a_sw_i2c_start();
// send slave address
a_send_byte(slave_addr << LEFT_SHIFT);
// wait ack
a_wait_ack();
// slave register address
a_send_byte(register_addr);
// wait ack
a_wait_ack();
// read data
a_send_byte(data);
// wait ack
a_wait_ack();
// send stop signal
a_sw_i2c_stop();
}
(5)多字节读\写接口函数。
/* read multiple bytes data*/
uint8_t a_sw_i2c_read_bytes(uint8_t slave_addr, uint8_t register_addr, uint8_t *data, uint8_t data_size)
{
uint8_t i;
//uint8_t register_data[i];
// send start signal
a_sw_i2c_start();
// send slave address write
a_send_byte(slave_addr << LEFT_SHIFT);
// wait ack
a_wait_ack();
// slave register address
a_send_byte(register_addr);
// wait ack
a_wait_ack();
// send start signal
a_sw_i2c_start();
// send slave address read
a_send_byte(slave_addr << LEFT_SHIFT + READ_BIT);
// wait ack
a_wait_ack();
// read multiple bytes
for (i = 0; i < data_size; i++) {
data[i] = a_receive_byte();
if (i < data_size - 1) {
a_ack();
}
}
// read completed send nack signal
a_nack();
// send stop signal
a_sw_i2c_stop();
}
/* write multiple bytes data*/
uint8_t a_sw_i2c_write_bytes(uint8_t slave_addr, uint8_t register_addr, uint8_t *data, uint8_t data_size)
{
uint8_t i;
// send start signal
a_sw_i2c_start();
// send slave address write
a_send_byte(slave_addr << LEFT_SHIFT);
// wait ack
a_wait_ack();
// slave register address
a_send_byte(register_addr);
// wait ack
a_wait_ack();
// write multiple bytes
for (i = 0; i < data_size; i++) {
a_send_byte(data[i]);
if (i < data_size - 1) {
aw_ack();
}
}
// send stop signal
a_sw_i2c_stop();
}
二、创建a_sw_i2c.h头文件
#ifndef __IIC_H
#define __IIC_H
#include "systick.h"
// define sw_i2c gpio
#define SCL_HIGH (GPIO_BOP(GPIOB) = BIT(6))
#define SCL_LOW (GPIO_BC(GPIOB) = BIT(6))
#define SDA_HIGH (GPIO_BOP(GPIOB) = BIT(7))
#define SDA_LOW (GPIO_BC(GPIOB) = BIT(7))
#define SDA_READ (GPIO_ISTAT(GPIOB) & BIT(7))
#define SLAVE_ADDR (0x45)
#define LEFT_SHIFT (1)
#define READ_BIT (1)
#define HIGH_BIT (0x80)
#define ONE_BYTE (1)
#define TWO_BYTE (2)
#define THREE_BYTE (3)
#define FOUR_BYTE (4)
//sw
void a_i2c_gpio_config(void);
void a_sw_i2c_start(void);
void a_sw_i2c_stop(void);
void a_ack(void);
void a_nack(void);
uint8_t a_wait_ack(void);
void a_send_byte(uint8_t byte);
uint8_t a_receive_byte(void);
/* read one byte data */
uint8_t a_sw_i2c_read_byte(uint8_t slave_addr, uint8_t register_addr, uint8_t data);
/* write one byte data */
uint8_t a_sw_i2c_write_byte(uint8_t slave_addr, uint8_t register_addr, uint8_t data);
/* read multiple bytes data */
uint8_t a_sw_i2c_read_bytes(uint8_t slave_addr, uint8_t register_addr, uint8_t *data, uint8_t data_size);
/* write multiple bytes data */
uint8_t a_sw_i2c_write_bytes(uint8_t slave_addr, uint8_t register_addr, uint8_t *data, uint8_t data_size);
#endif
三、创建a_sw_i2c.h头文件
while (1) {
uint8_t buffer_write[1] = {0x02};
uint8_t buffer_read[2] = {0};
uint8_t data_read = 0;
uint8_t data_write = 0x02;
// hardware i2c write bytes
a_hw_i2c_write_bytes(SLAVE_ADDR, REGISTER_RSTR, buffer_write, ONE_BYTE);
// hardware i2c read bytes
a_hw_i2c_read_bytes(SLAVE_ADDR, REGISTER_RSTR, buffer_read, TWO_BYTE);
a_delay_1s;
// sardware i2c read one byte
a_sw_i2c_read_byte(SLAVE_ADDR, REGISTER_RSTR, data_read);
// sardware i2c write one byte
a_sw_i2c_write_byte(SLAVE_ADDR, REGISTER_RSTR, data_write);
// sardware i2c read multiple bytes
a_sw_i2c_read_bytes(SLAVE_ADDR, REGISTER_RSTR, buffer_read, TWO_BYTE);
a_delay_1s;
}