| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /*********************************************************************************************************
- * 模块名称: I2C.c
- * 摘 要: I2C模块
- * 当前版本: 1.0.0
- * 作 者:Leyutek(COPYRIGHT 2018 - 2021 Leyutek. All rights reserved.)
- * 完成日期:2021年07月01日
- * 内 容:
- * 注 意: none
- **********************************************************************************************************
- * 取代版本:
- * 作 者:
- * 完成日期:
- * 修改内容:
- * 修改文件:
- *********************************************************************************************************/
- /*********************************************************************************************************
- * 包含头文件
- *********************************************************************************************************/
- #include "I2c.h"
- #include "SysTick.h"
- /*********************************************************************************************************
- * 宏定义
- *********************************************************************************************************/
- //读取SDA端口
- #define READ_I2CSDA() gpio_input_bit_get(GPIOF, GPIO_PIN_0)
-
- //时钟线SCL输出高电平
- #define SET_I2C_SCL() gpio_bit_set(GPIOF, GPIO_PIN_1)
- //时钟线SCL输出低电平
- #define CLR_I2C_SCL() gpio_bit_reset(GPIOF, GPIO_PIN_1)
- //数据线SDA输出高电平
- #define SET_I2C_SDA() gpio_bit_set(GPIOF, GPIO_PIN_0)
- //数据线SDA输出低电平
- #define CLR_I2C_SDA() gpio_bit_reset(GPIOF, GPIO_PIN_0)
- /*********************************************************************************************************
- * 内部变量
- *********************************************************************************************************/
- /*********************************************************************************************************
- * 内部函数声明
- *********************************************************************************************************/
- static void ConfigI2CGPIO(void); //配置I2C的GPIO
- static void SetI2CSDAAsInput(void); //将SDA端设置为输入
- static void SetI2CSDAAsOutput(void); //将SDA端设置为输出
- /*********************************************************************************************************
- * 内部函数实现
- *********************************************************************************************************/
- /*********************************************************************************************************
- * 函数名称: ConfigI2CGPIO
- * 函数功能: 配置I2C的GPIO
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意:
- *********************************************************************************************************/
- static void ConfigI2CGPIO(void)
- {
- //配置PC15
- rcu_periph_clock_enable(RCU_GPIOC); //使能GPIOC的时钟
- gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_13); //设置GPIO模式
- gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_13); //设置GPIO输出模式及速度
- gpio_bit_reset(GPIOC, GPIO_PIN_13); //将PC15默认状态设置为低电平,24C64可以读写
-
- //配置PF0
- rcu_periph_clock_enable(RCU_GPIOF); //使能GPIOF的时钟
- gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0); //设置GPIO模式
- gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_0); //设置GPIO输出模式及速度 SDA
-
- //配置PF1
- gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_1); //设置GPIO模式
- gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_1); //设置GPIO输出模式及速度 SCL
- }
- /*********************************************************************************************************
- * 函数名称: SetI2CSDAAsInput
- * 函数功能: 将SDA端设置为输入
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意:
- *********************************************************************************************************/
- static void SetI2CSDAAsInput(void)
- {
- gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0); //配置PF0为输入模式
- }
- /*********************************************************************************************************
- * 函数名称: SetI2CSDAAsOutput
- * 函数功能: 将SDA端设置为输出
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意:
- *********************************************************************************************************/
- static void SetI2CSDAAsOutput(void)
- {
- gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0); //设置PF0为输出模式
- gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0); //设置输出模式及速度
- gpio_bit_reset(GPIOC, GPIO_PIN_15); //状态默认设置为高电平
- }
- /*********************************************************************************************************
- * API函数实现
- *********************************************************************************************************/
- /*********************************************************************************************************
- * 函数名称: InitI2C
- * 函数功能: 初始化IIC
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意:
- *********************************************************************************************************/
- void InitI2C(void)
- {
- ConfigI2CGPIO(); //配置I2C的GPIO
- }
- /*********************************************************************************************************
- * 函数名称: GenI2CStartSig
- * 函数功能: 产生IIC起始时序,准备发送或接收数据前必须由起始序列开始
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意: SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据
- * 生成下图所示的波形图,即为起始时序
- * 1 2 3 4
- * __________
- * SCL : __/ \_____
- * ________
- * SDA : \___________
- *********************************************************************************************************/
- void GenI2CStartSig(void)
- {
- SetI2CSDAAsOutput(); //将数据线SDA设置为输出
- SET_I2C_SDA(); //1#数据线SDA输出高电平
- SET_I2C_SCL(); //2#时钟线SCL输出高电平,2-3之间的间隔须>4.7us
- DelayNus(4); //延时4us
- CLR_I2C_SDA(); //3#数据线SDA输出低电平,3-4之间的间隔须>4.0us
- DelayNus(4); //延时4us
- CLR_I2C_SCL(); //4#时钟线SCL输出低电平,保持I2C的时钟线SCL为低电平,准备发送或接收数据
- }
- /*********************************************************************************************************
- * 函数名称: GenI2CStopSig
- * 函数功能: 产生IIC停止时序
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意: SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据
- * 生成下图所示的波形图,即为停止时序
- * 1 2 3 4
- * _______________
- * SCL : ______/
- * __ ____________
- * SDA: \______/
- *********************************************************************************************************/
- void GenI2CStopSig(void)
- {
- SetI2CSDAAsOutput(); //将数据线SDA设置为输出
- CLR_I2C_SCL(); //1#时钟线SCL输出低电平
- CLR_I2C_SDA(); //2#数据线SDA输出低电平
- DelayNus(4); //延时4us
- SET_I2C_SCL(); //3#时钟线SCL输出高电平,3-4之间的间隔须>4.7us
- SET_I2C_SDA(); //4#数据线SDA输出高电平,发送I2C总线结束信号,4之后SDA须保持不小于4.0us的高电平
- DelayNus(4); //延时4us
- }
- /*********************************************************************************************************
- * 函数名称: I2CSendByte
- * 函数功能: 发送一字节,数据从高位开始发送出去
- * 输入参数: txd
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意: 下面是具体的时序图
- * 1 2 3 4
- * ______
- * SCL: ________/ \______
- * ______________________
- * SDA: \\\___________________
- *********************************************************************************************************/
- void I2CSendByte(unsigned char txd)
- {
- unsigned char t; //循环计数器
-
- SetI2CSDAAsOutput(); //将数据线SDA设置为输出
- CLR_I2C_SCL(); //1#时钟线SCL输出低电平,开始数据传输
-
- for(t = 0; t < 8; t++) //循环8次,从高到低取出字节的8个位
- {
- if((txd&0x80) >> 7) //2#取出字节最高位,并判断为‘0’还是‘1’,从而做出相应的操作
- {
- SET_I2C_SDA(); //数据线SDA输出高电平,数据位为‘1’
- }
- else
- {
- CLR_I2C_SDA(); //数据线SDA输出低电平,数据位为‘0’
- }
-
- txd <<= 1; //左移一位,次高位移到最高位
-
- DelayNus(2); //延时2us
- SET_I2C_SCL(); //3#时钟线SCL输出高电平
- DelayNus(2); //延时2us
- CLR_I2C_SCL(); //4#时钟线SCL输出低电平
- DelayNus(2); //延时2us
- }
- }
- /*********************************************************************************************************
- * 函数名称: I2CReadByte
- * 函数功能: 读取一字节数据,当ack=1时,发送ACK,当ack=0时,发送nACK
- * 输入参数: txd
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意: 下面是具体的时序图
- * ______
- * SCL: ______/ \___
- * ____________________
- * SDA: \\\\______________\\\
- *********************************************************************************************************/
- unsigned char I2CReadByte(unsigned char ack)
- {
- unsigned char i = 0; //i为循环计数器
- unsigned char receive = 0; //receive用来存放接收的数据
-
- SetI2CSDAAsInput(); //1#将数据线SDA设置为输入
-
- for(i = 0; i < 8; i++ ) //循环8次,从高到低读取字节的8个位
- {
- CLR_I2C_SCL(); //2#时钟线SCL输出低电平
- DelayNus(2); //延时2us
- SET_I2C_SCL(); //3#时钟线SCL输出高电平
-
- receive <<= 1; //左移一位,空出新的最低位
-
- if(READ_I2CSDA()) //4#读取数据线SDA的数据位
- {
- receive++; //在SCL的上升沿后,数据已经稳定,因此可以取该数据,存入最低位
- }
-
- DelayNus(1); //延时1us
- }
-
- if(NACK == ack) //如果ack为NACK
- {
- SendI2CNAck(); //发送NACK,无应答
- }
- else //如果ack为ACK
- {
- SendI2CAck(); //发送ACK,应答
- }
-
- return receive; //返回读取到的数据
- }
- /*********************************************************************************************************
- * 函数名称: I2CWaitAck
- * 函数功能: 等待应答信号到来
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: 1,接收应答失败;0,接收应答成功
- * 创建日期: 2021年07月01日
- * 注 意: 当SDA拉低后,表示接收到ACK信号,然后,拉低SCL,根据实际情况做出是否继续传递信号的判断
- * 此处表示发送端收到接收端的ACK
- * _______|____
- * SCL: | \_________
- * _______|
- * SDA: \_____________
- *********************************************************************************************************/
- unsigned char I2CWaitAck(void)
- {
- unsigned char ucErrTime = 0;
-
- SetI2CSDAAsInput(); //将数据线SDA设置为输出
- SET_I2C_SCL(); //时钟线SCL输出高电平
- DelayNus(1); //延时1us
-
- while(READ_I2CSDA()) //读回来的数据如果是高电平,即接收端没有应答
- {
- ucErrTime++; //计数器加1
-
- if(ucErrTime > 250) //如果超过250次,则判断为接收端出现故障,因此发送结束信号
- {
- GenI2CStopSig(); //产生一个停止信号
-
- return 1; //返回值为1,表示没有收到应答信号
- }
- }
-
- CLR_I2C_SCL(); //表示已收到应答信号,时钟线SCL输出低电平,钳住I2C总线
-
- return 0; //返回值为0,表示接收应答成功
- }
- /*********************************************************************************************************
- * 函数名称: SendI2CAck
- * 函数功能: 发送应答信号
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意: 下面是具体的时序图
- * 1 2 3 4
- * ______
- * SCL: ________/ \______
- * __
- * SDA: \___________________
- *********************************************************************************************************/
- void SendI2CAck(void)
- {
- CLR_I2C_SCL(); //1#时钟线SCL输出低电平,开始传送应答信号
- SetI2CSDAAsOutput(); //将数据线SDA设置为输出
- CLR_I2C_SDA(); //2#数据线SDA输出低电平
- DelayNus(2); //延时2us
- SET_I2C_SCL(); //3#时钟线SCL输出高电平,在SCL上升沿前就要把SDA拉低,为应答信号
- DelayNus(2); //延时2us
- CLR_I2C_SCL(); //4#时钟线SCL输出低电平,钳住I2C总线
- }
- /*********************************************************************************************************
- * 函数名称: SendI2CNAck
- * 函数功能: 不发送应答信号
- * 输入参数: void
- * 输出参数: void
- * 返 回 值: void
- * 创建日期: 2021年07月01日
- * 注 意: 下面是具体的时序图
- * 1 2 3 4
- * ______
- * SCL: ________/ \______
- * __ ___________________
- * SDA: __/
- *********************************************************************************************************/
- void SendI2CNAck(void)
- {
- CLR_I2C_SCL(); //1#拉低时钟线,开始传送非应答信号
- SetI2CSDAAsOutput(); //将数据线SDA设置为输出
- SET_I2C_SDA(); //2#数据线SDA输出高电平
- DelayNus(2); //延时2us
- SET_I2C_SCL(); //3#时钟线SCL输出高电平,在SCL上升沿前就要把SDA拉高,为非应答信号
- DelayNus(2); //延时2us
- CLR_I2C_SCL(); //4#时钟线SCL输出低电平,钳住I2C总线
- }
|