驱动代码:
iic_driver.c:
#include "iic-driver.h"
/*
*********************************************************************************************************
* 函 数 名: i2c_Delay
* 功能说明: I2C总线位延迟,最快400KHz
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < CycleCount; i++);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_start
* 功能说明: CPU发起I2C总线启动信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_start(iic_node *iic){
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin);
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
iic->sda_off(iic->GPIOx,iic->GPIO_SDA_Pin);
i2c_Delay();
iic->scl_off(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_start
* 功能说明: CPU发起I2C总线停止信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(iic_node *iic){
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
iic->sda_off(iic->GPIOx,iic->GPIO_SDA_Pin);
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参:_ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(iic_node *iic,uint8_t _ucByte){
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++){
if (_ucByte & 0x80){
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin);
}else{
iic->sda_off(iic->GPIOx,iic->GPIO_SDA_Pin);
}
i2c_Delay();
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
iic->scl_off(iic->GPIOx,iic->GPIO_SCL_Pin);
if (i == 7){
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
i2c_Delay();
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参:无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(iic_node *iic){
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++){
value <<= 1;
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
if (iic->get_sda_value(iic->GPIOx,iic->GPIO_SDA_Pin)){
value++;
}
iic->scl_off(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
}
return value;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参:无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(iic_node *iic){
uint8_t re;
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin); /* CPU释放SDA总线 */
i2c_Delay();
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay();
if (iic->get_sda_value(iic->GPIOx,iic->GPIO_SDA_Pin)){ /* CPU读取SDA口线状态 */
re = 1;
}else{
re = 0;
}
iic->scl_off(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
return re;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(iic_node *iic){
iic->sda_off(iic->GPIOx,iic->GPIO_SDA_Pin); /* CPU驱动SDA = 0 */
i2c_Delay();
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin); /* CPU产生1个时钟 */
i2c_Delay();
iic->scl_off(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(iic_node *iic){
iic->sda_on(iic->GPIOx,iic->GPIO_SDA_Pin); /* CPU驱动SDA = 1 */
i2c_Delay();
iic->scl_on(iic->GPIOx,iic->GPIO_SCL_Pin); /* CPU产生1个时钟 */
i2c_Delay();
iic->scl_off(iic->GPIOx,iic->GPIO_SCL_Pin);
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CheckDevice
* 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
* 形 参:_Address:设备的I2C总线地址
* 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(iic_node *iic,uint8_t _Address){
uint8_t ucAck = 1;
i2c_start(iic); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendByte(iic,_Address | I2C_WR);
ucAck = i2c_WaitAck(iic); /* 检测设备的ACK应答 */
i2c_Stop(iic); /* 发送停止信号 */
return ucAck;
}
/*
*********************************************************************************************************
* 函 数 名: iic_device_checkok
* 功能说明: 判断IIC设备是否正常
* 形 参:无
* 返 回 值: 1 表示正常, 0 表示不正常
*********************************************************************************************************
*/
uint8_t iic_device_checkok(iic_node *iic,uint8_t addr){
if (i2c_CheckDevice(iic,addr) == 0){
return 1;
}else{
/* 失败后,切记发送I2C总线停止信号 */
i2c_Stop(iic);
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: iic_write_byte
* 功能说明: 向从机设备写入一个字节
* 形 参:value:要写入的数据 addr:寄存器的地址
* 返 回 值: void
*********************************************************************************************************
*/
void iic_write_byte(iic_node *iic,uint8_t addr,uint8_t regster_addr,uint8_t value){
i2c_start(iic);
i2c_SendByte(iic,addr | I2C_WR);
i2c_WaitAck(iic);
i2c_SendByte(iic,regster_addr);
i2c_WaitAck(iic);
i2c_SendByte(iic,value);
i2c_WaitAck(iic);
i2c_Stop(iic);
}
/*
*********************************************************************************************************
* 函 数 名: iic_read_bytes
* 功能说明: 读取多个字节的数据
* 形 参:
* iic : 设备
* addr:设备地址
* _usAddress : 起始地址
* _usSize : 要读多少字节数据
* buff : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t iic_read_bytes( iic_node *iic,uint8_t addr,uint16_t _usAddress,uint8_t *buff,uint8_t _usSize){
uint8_t i = 0;
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
i2c_start(iic);
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(iic,addr | I2C_WR); /* 此处是写指令 */
/* 第3步:等待ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送寄存器地址,表示要读取的寄存器地址 */
i2c_SendByte(iic,(uint8_t)_usAddress);
/* 第5步:等待ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
i2c_start(iic);
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(iic,addr | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第9步:循环读取数据 */
for (i = 0; i < _usSize; i++)
{
buff[i] = i2c_ReadByte(iic); /* 读1个字节 */
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
if (i != _usSize - 1)
{
i2c_Ack(iic); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
}
else
{
i2c_NAck(iic); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
}
}
/* 发送I2C总线停止信号 */
i2c_Stop(iic);
return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop(iic);
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: iic_read_two_byte
* 功能说明: 读取两个字节的数据
* 形 参: _usAddress : 起始地址
* _usSize : 要读多少字节数据
* _pReadBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint16_t iic_read_two_byte( iic_node *iic,uint8_t addr,uint16_t _usAddress)
{
uint16_t temp = 0;
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
i2c_start(iic);
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(iic,addr | I2C_WR); /* 此处是写指令 */
/* 第3步:等待ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送寄存器地址,表示要读取的寄存器地址 */
i2c_SendByte(iic,(uint8_t)_usAddress);
/* 第5步:等待ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
i2c_start(iic);
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(iic,addr | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
temp =(i2c_ReadByte(iic) << 8); /* 读1个字节 */
i2c_Ack(iic); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
temp |=(i2c_ReadByte(iic) & 0xff);
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
i2c_NAck(iic); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
/* 发送I2C总线停止信号 */
i2c_Stop(iic);
return temp; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop(iic);
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: iic_read_one_byte
* 功能说明: 读取1个字节的数据
* 形 参: _usAddress : 起始地址
* _usSize : 要读多少字节数据
* _pReadBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t iic_read_one_byte( iic_node *iic,uint8_t addr,uint16_t _usAddress){
uint8_t temp = 0;
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
i2c_start(iic);
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(iic,addr | I2C_WR); /* 此处是写指令 */
/* 第3步:等待ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送寄存器地址,表示要读取的寄存器地址 */
i2c_SendByte(iic,(uint8_t)_usAddress);
/* 第5步:等待ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
i2c_start(iic);
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(iic,addr | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck(iic) != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
temp =i2c_ReadByte(iic)&0xff; /* 读1个字节 */
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
i2c_NAck(iic); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
/* 发送I2C总线停止信号 */
i2c_Stop(iic);
return temp; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop(iic);
return 0;
}
iic_driver.h
#ifndef _BSP_I2C_GPIO_H
#define _BSP_I2C_GPIO_H
#include "stm32f10x.h"
#define I2C_WR 0 /* 写控制bit */
#define I2C_RD 1 /* 读控制bit */
//循环次数,用来决定iic的延时时间
#define CycleCount 10
//定义IIC设备结构体类型
typedef struct {
uint8_t flag; //预留标志位
GPIO_TypeDef* GPIOx;
uint16_t GPIO_SDA_Pin;
uint16_t GPIO_SCL_Pin;
void (*scl_on)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*scl_off)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*sda_on)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*sda_off)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint8_t (*get_sda_value)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
}iic_node;
void i2c_start(iic_node *iic);
void i2c_Stop(iic_node *iic);
void i2c_SendByte(iic_node *iic,uint8_t _ucByte);
uint8_t i2c_ReadByte(iic_node *iic);
uint8_t i2c_WaitAck(iic_node *iic);
void i2c_Ack(iic_node *iic);
void i2c_NAck(iic_node *iic);
uint8_t i2c_CheckDevice(iic_node *iic,uint8_t _Address);
uint8_t iic_device_checkok(iic_node *iic,uint8_t addr);
void iic_write_byte(iic_node *iic,uint8_t addr,uint8_t regster_addr,uint8_t value);
uint8_t iic_read_bytes( iic_node *iic,uint8_t addr,uint16_t _usAddress,uint8_t *buff,uint8_t _usSize);
uint16_t iic_read_two_byte( iic_node *iic,uint8_t addr,uint16_t _usAddress);
uint8_t iic_read_one_byte( iic_node *iic,uint8_t addr,uint16_t _usAddress);
#endif
设备代码:(以SD2508时钟芯片为例)
sd2508.c
#include "sd2058.h"
iic_node iic_sd2058 = {
.GPIOx = GPIOC,
.GPIO_SDA_Pin = IIC_SDA_PIN,
.GPIO_SCL_Pin = IIC_SCK_PIN,
.scl_on = &GPIO_SetBits,
.scl_off = &GPIO_ResetBits,
.sda_on = &GPIO_SetBits,
.sda_off = &GPIO_ResetBits,
.get_sda_value = &GPIO_ReadInputDataBit,
};
/**********************************************************************
* @ 函数名 : iic_gpio_init
* @ 功能说明:
* @ 参数 :
* @ 返回值 :
********************************************************************/
void iic_gpio_init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( IIC_PORT_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN |IIC_SCK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SDA_GPIO, &GPIO_InitStructure);
i2c_Stop(&iic_sd2058);
}
/**********************************************************************
* @ 函数名 : check_at24c02_device
* @ 功能说明: 检测at24c02是否在线
* @ 参数 :
* @ 返回值 :
********************************************************************/
uint8_t check_sd2058_device(iic_node *driver,uint8_t device_addr){
return i2c_CheckDevice(driver,device_addr);
}
/**********************************************************************
* @ 函数名 : write_time_on
* @ 功能说明: 时钟芯片写开锁
* @ 参数 :
* @ 返回值 :
********************************************************************/
static uint8_t write_time_on(iic_node *driver){
if(check_sd2058_device(driver,SD2058_ADDR) != 0)return 0;
iic_write_byte(driver,SD2058_ADDR,0x10,0x80);
iic_write_byte(driver,SD2058_ADDR,0x0f,0x84);
return 1;
}
/**********************************************************************
* @ 函数名 : write_time_off
* @ 功能说明: 时钟芯片写上锁
* @ 参数 :
* @ 返回值 :
********************************************************************/
static uint8_t write_time_off(iic_node *driver){
if(check_sd2058_device(driver,SD2058_ADDR) != 0)return 0;
iic_write_byte(driver,SD2058_ADDR,0x0f,0);
iic_write_byte(driver,SD2058_ADDR,0x10,0);
return 1;
}
/**********************************************************************
* @ 函数名 : write_sd2058_time
* @ 功能说明: 向sd2058内部更新时间
* @ 参数 :
* @ 返回值 :
********************************************************************/
uint8_t write_sd2058_time(iic_node *driver,Time_Struct *time_value){
if(write_time_on(driver)){
i2c_start(driver);
i2c_SendByte(driver,SD2058_ADDR | I2C_WR);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,0);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->second);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->min);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->hour | 0x80);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->week);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->day);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->month);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_SendByte(driver,time_value->year);
if(i2c_WaitAck(driver)){
return 0;
}
i2c_Stop(driver);
write_time_off(driver);
return 1;
}
return 0;
}
/**********************************************************************
* @ 函数名 : read_sd2058_time
* @ 功能说明: 读取sd2058内部时间
* @ 参数 :
* @ 返回值 :
********************************************************************/
void read_sd2058_time(iic_node *driver,Time_Struct *time_value){
uint8_t buff[7] = {0};
iic_read_bytes(driver,SD2058_ADDR,0,buff,7);
time_value->second = buff[0];
time_value->min = buff[1];
time_value->hour = buff[2] & 0x7F;
time_value->week = buff[3];
time_value->day = buff[4];
time_value->month = buff[5];
time_value->year = buff[6];
}
/**********************************************************************
* @ 函数名 : comp_time_size
* @ 功能说明: 比较时间段
* @ 参数 :
* @ 返回值 :
********************************************************************/
uint8_t comp_time_size(Time_Struct *time_value,time_t at_time,uint8_t begin_hour,uint8_t begin_min,uint8_t end_hour,uint8_t end_min){
time_t begin_time = 0,end_time = 0;
struct tm temp = {
.tm_year = (time_value->year >> 4) * 10 + (time_value->year & 0x0F)+2000,
.tm_mon = ((time_value->month & 0x10) >> 4) * 10 + (time_value->month & 0x0F),
.tm_mday = ((time_value->day & 0x30) >> 4) * 10 + (time_value->day & 0x0F),
.tm_hour = ((begin_hour & 0x70) >> 4) * 10 + (begin_hour & 0x0F),
.tm_min = ((begin_min & 0x70) >> 4) * 10 + (begin_min & 0x0F),
.tm_sec = ((time_value->second & 0x70) >> 4) * 10 + (time_value->second & 0x0F),
};
begin_time = mktime(temp);
temp.tm_hour = ((end_hour & 0x70) >> 4) * 10 + (end_hour & 0x0F),
temp.tm_min = ((end_min & 0x70) >> 4) * 10 + (end_min & 0x0F),
end_time = mktime(temp);
if((at_time > begin_time) && (at_time < end_time))
return 1;
else
return 0;
}
sd2058.h
#ifndef __SD2058_H_
#define __SD2058_H_
#include "stm32f10x.h"
#include "iic-driver.h"
#include "localtime.h"
#define SD2058_ADDR 0x64
#define IIC_PORT_CLK RCC_APB2Periph_GPIOC
#define IIC_SDA_GPIO GPIOC
#define IIC_SDA_PIN GPIO_Pin_2
#define IIC_SCK_GPIO GPIOC
#define IIC_SCK_PIN GPIO_Pin_3
typedef struct{
uint8_t second;
uint8_t min;
uint8_t hour;
uint8_t week;
uint8_t day;
uint8_t month;
uint8_t year;
}Time_Struct;
extern iic_node iic_sd2058;
extern Time_Struct Time_Data;
uint8_t check_sd2058_device(iic_node *driver,uint8_t device_addr);
uint8_t write_sd2058_time(iic_node *driver,Time_Struct *time_value);
void read_sd2058_time(iic_node *driver,Time_Struct *time_value);
uint8_t comp_time_size(Time_Struct *time_value,time_t at_time,uint8_t begin_hour,uint8_t begin_min,uint8_t end_hour,uint8_t end_min);
#endif
IIC驱动代码为两个两个文件,一个是iic_driver.c与其头文件iic_driver.h。
iic_driver.c存放的是驱动的实现,我们主要关心的是iic_driver.h这个头文件,因为不管是我们做iic通讯还是移植到其他32设备上,主要关心的只是iic_driver.h头文件,而iic_driver.c我们一般不需要动他。
在iic_driver.h中,设备结构体是实现iic关联硬件GPIO口的关键,该结构体实现了硬件GPIO的关联与GPIO的读写。
//定义IIC设备结构体类型
typedef struct {
uint8_t flag;
GPIO_TypeDef* GPIOx;
uint16_t GPIO_SDA_Pin;
uint16_t GPIO_SCL_Pin;
void (*scl_on)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*scl_off)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*sda_on)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*sda_off)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint8_t (*get_sda_value)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
}iic_node;
我们在编写IIC设备代码时,只需要初始化这个结构体实现IIC设备硬件连接的GPIO就实现了硬件与设备的关联。
iic_node iic_sd2058 = {
.GPIOx = GPIOC,
.GPIO_SDA_Pin = IIC_SDA_PIN,
.GPIO_SCL_Pin = IIC_SCK_PIN,
.scl_on = &GPIO_SetBits, //标准库中的GPIO置1函数
.scl_off = &GPIO_ResetBits, //标准库中的GPIO复0函数
.sda_on = &GPIO_SetBits,
.sda_off = &GPIO_ResetBits,
.get_sda_value = &GPIO_ReadInputDataBit, //标准库中的GPIO读取位值函数
};
第二步实现GPIO的初始化:(将IIC引脚初始化成开漏状态)
/**********************************************************************
* @ 函数名 : iic_gpio_init
* @ 功能说明:
* @ 参数 :
* @ 返回值 :
********************************************************************/
void iic_gpio_init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( IIC_PORT_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN |IIC_SCK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SDA_GPIO, &GPIO_InitStructure);
i2c_Stop(&iic_sd2058);
}
第三步调用iic_driver.h中的各各函数开始实现iic设备的读写操作。具体怎么操作可以翻看以下上面的sd2058.c的实现。很简单。
以上是基于STM32F103标准库的实现与使用。下面讲一下如何对这个iic驱动文件进行移植。该驱动的移植也很简单,需要改动两个地方,第一个是iic_driver.h中的
#define CycleCount 10
该宏定义定义了iic驱动通讯频率,该值由自己单片机的自身的主频而定,请自行计算。
第二个需要更改的地方为iic_driver.h中的iic设备结构体。
//定义IIC设备结构体类型
typedef struct {
uint8_t flag;
GPIO_TypeDef* GPIOx;
uint16_t GPIO_SDA_Pin;
uint16_t GPIO_SCL_Pin;
void (*scl_on)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*scl_off)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*sda_on)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (*sda_off)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint8_t (*get_sda_value)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
}iic_node;
以移植到hal库为例:(我使用的是stm32l051c8)
首先打开HAL库的GPIO对应的头文件找到我们需要用到的gpio设置函数与gpio读取函数:
然后根据hal库的GPIO设置函数与读取函数的声明来更改iic_driver.h中的设备结构体:
(注:要看一下要移植芯片的
GPIO_TypeDef* GPIOx;
uint16_t GPIO_SDA_Pin;
uint16_t GPIO_SCL_Pin;
的具体l类型,要适应对应的芯片)
更改完成之后,就可以编写GPIO初始化,设备的读写函数了。跟上面的sd2058的步骤一样了。
需要注意的是: HAL库中的GPIO设置函数只有一个,也就是说GPIO置1和置0函数只有一个。
而 我们的iic结构体中是有gpio的on和off操作的。所以在移植到这个芯片的时候,我不得不改一下iic_driver.c文件以适应该驱动。我的更改如下:
在iic_driver中的驱动函数中增加GPIO_PIN_SET和GPIO_PIN_RESET参数。但是如果我们移植的是GD系列的,或其他国产芯片系列的芯片,GPIO的置1和置0操作是分开的时候,iic_driver.c是不需要动的,我们只需要将厂商提供的操作库中的gpio操作头文件的gpio置1和gpio置0和读取gpio的值的函数声明复制然后粘贴到iic_driver.h中的iic设备结构体中的相应函数上即可,移植起来很是方便。