因项目需要控制其他芯片进行ADC采集,协议用的是IIC,需要用到IO模拟IIC,特此记录,已经过测试,但由于个人水平不够导致芯片使用不规范没有采集到合理的数值(ADS7830)。本篇为个人编程记录,其中也掺杂自己的理解,可能有错误之处,敬请斧正!!
由于IIC是通过SDA进行单线通信,所以使用过程中需要重新配置SDA引脚的输入输出模式,实现数据的收发。SDA和SCL线处于空闲状态时候(高电平),SDA线先拉低,SCL线后拉低表示起始信号;SDA线先拉高,SCL线后拉高表示停止信号,其间,SCL拉低的时候SDA线准备数据(0/1),在SCL线拉高时表示SDA的数据有效,接收应答前需要将SDA的引脚设置为输入模式,接收之后恢复成输出模式。
宏定义:
#define GPIO_SCL_PIN GET_PIN(F,1)
#define GPIO_SDA_PIN GET_PIN(F,0)
#define I2C_SCL(x) rt_pin_write(GPIO_SCL_PIN, x)
#define I2C_SDA(x) rt_pin_write(GPIO_SDA_PIN, x)
#define RD_I2C_SDA rt_pin_read(GPIO_SDA_PIN)
#define SW_SCL_DLY (300)
io配置:
void I2C_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
void I2C_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
void I2C_Init(void)
{
__HAL_RCC_GPIOF_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1, GPIO_PIN_SET);
}
IIC用到的延时采用的是硬延时__NOP()
void I2C_DELAY(uint32_t count)
{
for(; count > 0; count --)
{
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
}
}
重要的是模拟出信号跳变,使用IO口模拟出起始、停止信号
void I2C_START(void)
{
I2C_SDA(1);
I2C_DELAY(SW_SCL_DLY >> 1);
I2C_SCL(1);
I2C_DELAY(SW_SCL_DLY);
I2C_SDA(0);
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(0);
}
void I2C_STOP(void)
{
I2C_SCL(0);
I2C_SDA(0);
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(1);
I2C_DELAY(SW_SCL_DLY);
I2C_SDA(1);
I2C_DELAY(SW_SCL_DLY); //增加的延时
}
应答相关函数:
uint8_t I2C_WAIT_ACK(void)
{
uint8_t Wait_cnt = 255;
I2C_SCL(0);
I2C_SDA_IN();
I2C_SDA(0);
I2C_SCL(1); //产生第九个时钟脉冲,用于接收应答
while(RD_I2C_SDA != 0){
Wait_cnt--;
if(Wait_cnt == 0){
break;
}
}
I2C_SCL(0);
I2C_SDA_OUT();
if(Wait_cnt == 0)
{
I2C_STOP();
}
return Wait_cnt;
}
void I2C_ACK(void)
{
I2C_SCL(0);
I2C_SDA(0);//发送低电平,响应从机
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(1);
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(0);
}
void I2C_NOT_ACK(void)
{
I2C_SCL(0);
I2C_SDA(1);//发送低电平,响应从机
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(1);
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(0);
}
IIC的读写函数:
uint8_t I2C_WRITE_BYTE(uint8_t dat)
{
uint8_t i;
I2C_DELAY(SW_SCL_DLY);
for(i = 0;i < 8;i++){
I2C_DELAY(W_SCL_DLY);
if(dat & 0x80){
I2C_SDA(1);
}else{
I2C_SDA(0);
}
dat <<= 1;
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(1);
I2C_DELAY(SW_SCL_DLY);
I2C_SCL(0);
}
i = I2C_WAIT_ACK();
return i;
}
uint8_t I2C_READ_BYTE(uint8_t ack)
{
uint8_t dat = 0;
I2C_SCL(0);
I2C_SDA_IN();
I2C_SDA(0);
I2C_DELAY(SW_SCL_DLY >> 1);
for(uint8_t i = 0;i < 8;i ++){
I2C_SCL(1);
I2C_DELAY(SW_SCL_DLY);
if(RD_I2C_SDA != 0){
dat |= 0x01;
dat <<= 1;
}
I2C_SCL(0);
I2C_DELAY(SW_SCL_DLY);
}
I2C_SDA_OUT();
if(ack != 0){
I2C_ACK();
}else{
I2C_NOT_ACK();
}
return dat;
}