嵌入式篇 | 通用模拟IO方式I2C主机协议框架
使用示例
本文以压力传感器GP6818D为例:
主函数
int main(void)
{
float temp, pressure;
delay_init();
GZP6818D_Init();
while(1)
{
GZP6818D_GetMeasure(&pressure, &temp); // 注:这个型号无内置温度。
printf("pressure:%0.2f temp:%0.2f", pressure, temp);
delay_ms(1000);
}
}
GP6818D驱动
/**
* @file gzp6818d.c
* @author lakun (lakun@qq.com)
* @brief
* @version 1.0
* @date 2024-10-14
*
* Copyright (c) 2024 by AnKun.
* All rights reserved.
*
*/
#include "gzp6818d.h"
#include "viic.h"
extern void delay_ms(uint32_t nms);
extern void delay_us(uint32_t nus);
#define SDA_GPIO_Port GPIOA
#define SDA_GPIO_Pin GPIO_Pin_4
#define SCL_GPIO_Port GPIOA
#define SCL_GPIO_Pin GPIO_Pin_0
void pinSclSet1(uint8_t state)
{
GPIO_WriteBit(SCL_GPIO_Port, SCL_GPIO_Pin, state ? Bit_SET : Bit_RESET);
}
void pinSdaSet1(uint8_t state)
{
GPIO_WriteBit(SDA_GPIO_Port, SDA_GPIO_Pin, state ? Bit_SET : Bit_RESET);
}
uint8_t pinSdaGet1(void)
{
return GPIO_ReadInputDataBit(SDA_GPIO_Port, SDA_GPIO_Pin) == Bit_SET ? 1 : 0;
}
void pinSdaDir(uint8_t dir)
{
#if 0 // 开漏模式无需切换IO方向
if(dir)
{
// 设置为输出模式
}else
{
// 设置为输入模式
}
#endif
}
VirtI2C_HandleTypeDef hvi2c1 =
{
.pinSclSet = pinSclSet1,
.pinSdaSet = pinSdaSet1,
.pinSdaGet = pinSdaGet1,
.pinSdaDir = pinSdaDir,
.delayUs = delay_us,
};
void VirtI2C_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = SCL_GPIO_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(SCL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = SDA_GPIO_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
}
void GZP6818D_Init(void)
{
VirtI2C_GPIO_Init();
}
int GZP6818D_GetMeasure(float *pressure, int *temp)
{
uint8_t buf[32] = {0};
volatile int tmp = 0;
VirtI2C_MasterWriteByte(&hvi2c1, GZP6818D_DEV_ADDR, 0x01, 0x01); // 启动采集
delay_ms(20);
buf[0] = VirtI2C_MasterReadByte(&hvi2c1, GZP6818D_DEV_ADDR, 0x04);
buf[1] = VirtI2C_MasterReadByte(&hvi2c1, GZP6818D_DEV_ADDR, 0x05);
buf[2] = VirtI2C_MasterReadByte(&hvi2c1, GZP6818D_DEV_ADDR, 0x06);
buf[3] = VirtI2C_MasterReadByte(&hvi2c1, GZP6818D_DEV_ADDR, 0x07);
buf[4] = VirtI2C_MasterReadByte(&hvi2c1, GZP6818D_DEV_ADDR, 0x08);
tmp = (buf[0] << 16) | (buf[1] << 8) | buf[2];
if(tmp >= 8388608)
{
tmp -= 16777216;
}
*pressure = (float)tmp / 2097152 * (700000 - 100000) + 100000;
tmp = (buf[3] << 8) | buf[4];
if(tmp > 32768)
{
tmp = tmp - 65536;
}
*temp = tmp;
return 0;
}
头文件
#ifndef __VIRT_IIC_H
#define __VIRT_IIC_H
#include <stdint.h>
#define VirtI2C_DelayUS 10
typedef struct
{
void (*pinSdaSet)(uint8_t state);
void (*pinSclSet)(uint8_t state);
uint8_t (*pinSdaGet)(void);
void (*pinSdaDir)(uint8_t dir);
void (*delayUs)(uint32_t microseconds);
}VirtI2C_HandleTypeDef;
void VirtI2C_MasterWriteByte(VirtI2C_HandleTypeDef* hi2c, uint8_t DevAddress, uint8_t MemAddress, uint8_t Data);
uint8_t VirtI2C_MasterReadByte(VirtI2C_HandleTypeDef* hi2c, uint8_t DevAddress, uint8_t MemAddress);
#endif
源文件
#include "viic.h"
//产生IIC起始信号
void VirtI2C_Start(VirtI2C_HandleTypeDef* hi2c)
{
hi2c->pinSdaDir(1); //sda线输出
hi2c->pinSdaSet(1);
hi2c->pinSclSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSdaSet(0);//START:when CLK is high,DATA change form high to low
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(0);//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void VirtI2C_Stop(VirtI2C_HandleTypeDef* hi2c)
{
hi2c->pinSdaDir(1);//sda线输出
hi2c->pinSclSet(0);
hi2c->pinSdaSet(0);//STOP:when CLK is high DATA change form low to high
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(1);
hi2c->pinSdaSet(1);//发送I2C总线结束信号
hi2c->delayUs(VirtI2C_DelayUS);
}
// 等待应答信号到来
// 返回值:0,接收应答失败
// 1,接收应答成功
uint8_t VirtI2C_Wait_Ack(VirtI2C_HandleTypeDef* hi2c)
{
uint8_t retry = 0;
hi2c->pinSdaDir(0); //SDA设置为输入
hi2c->pinSdaSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
while(hi2c->pinSdaGet())
{
if(++retry > VirtI2C_DelayUS)
{
VirtI2C_Stop(hi2c);
return 0;
}
hi2c->delayUs(1);
}
hi2c->pinSclSet(0);//时钟输出0
return 1;
}
//产生ACK应答
void VirtI2C_Ack(VirtI2C_HandleTypeDef* hi2c)
{
hi2c->pinSclSet(0);
hi2c->pinSdaDir(1);
hi2c->pinSdaSet(0);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(0);
}
//不产生ACK应答
void VirtI2C_NAck(VirtI2C_HandleTypeDef* hi2c)
{
hi2c->pinSclSet(0);
hi2c->pinSdaDir(1);
hi2c->pinSdaSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(0);
}
// IIC发送一个字节
void VirtI2C_Send_Byte(VirtI2C_HandleTypeDef* hi2c, uint8_t txd)
{
uint8_t t;
hi2c->pinSdaDir(1);
hi2c->pinSclSet(0);//拉低时钟开始数据传输
for(t = 0; t < 8; t++)
{
if((txd & 0x80) >> 7)
{
hi2c->pinSdaSet(1);
}
else
{
hi2c->pinSdaSet(0);
}
txd <<= 1;
hi2c->delayUs(VirtI2C_DelayUS); //对TEA5767这三个延时都是必须的
hi2c->pinSclSet(1);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(0);
hi2c->delayUs(VirtI2C_DelayUS);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t VirtI2C_Recv_Byte(VirtI2C_HandleTypeDef* hi2c, uint8_t ack)
{
uint8_t i, receive = 0;
hi2c->pinSdaDir(0);//SDA设置为输入
for(i = 0; i < 8; i++)
{
hi2c->pinSclSet(0);
hi2c->delayUs(VirtI2C_DelayUS);
hi2c->pinSclSet(1);
receive <<= 1;
if(hi2c->pinSdaGet())receive++;
hi2c->delayUs(VirtI2C_DelayUS);
}
if(!ack)
VirtI2C_NAck(hi2c);//发送nACK
else
VirtI2C_Ack(hi2c); //发送ACK
return receive;
}
void VirtI2C_MasterWriteByte(VirtI2C_HandleTypeDef* hi2c, uint8_t DevAddress, uint8_t MemAddress, uint8_t Data)
{
VirtI2C_Start(hi2c);
VirtI2C_Send_Byte(hi2c, DevAddress & 0xFE);
VirtI2C_Wait_Ack(hi2c);
VirtI2C_Send_Byte(hi2c, MemAddress);
VirtI2C_Wait_Ack(hi2c);
VirtI2C_Send_Byte(hi2c, Data);
VirtI2C_Wait_Ack(hi2c);
VirtI2C_Stop(hi2c);
}
uint8_t VirtI2C_MasterReadByte(VirtI2C_HandleTypeDef* hi2c, uint8_t DevAddress, uint8_t MemAddress)
{
uint8_t Data = 0;
VirtI2C_Start(hi2c);
VirtI2C_Send_Byte(hi2c, DevAddress & 0xFE);
VirtI2C_Wait_Ack(hi2c);
VirtI2C_Send_Byte(hi2c, MemAddress);
VirtI2C_Wait_Ack(hi2c);
VirtI2C_Start(hi2c);
VirtI2C_Send_Byte(hi2c, DevAddress | 0x01); // 切换为接收模式
VirtI2C_Wait_Ack(hi2c);
Data = VirtI2C_Recv_Byte(hi2c, 0);
VirtI2C_Stop(hi2c);
return Data;
}