- 一、实现的功能
- 二、根据功能实现代码
- 1、主文件main.c
- 2、I2C头文件“i2c.h”
- 3、I2c源文件“i2c.c”
- 三、实现功能过程的注意与学习点
- 1、注意点
- 2、学习的知识点
- ①往AT24C02存储器写入256个字节数据;
- ②读取写入AT24C02的字节数据;
- ③通过串口把AT24C02保存的数据打印输出;
- 实现效果:![在这里插入图片描述](https://img-blog.csdnimg.cn/
#include"key.h"
#include"led.h"
#include"lcd.h"
#include "usart.h"
#include "i2c.h"
unsigned char ucSec;
unsigned char pucBuf1[16],pucBuf2[256];
unsigned long ulTick_ms;
int main(void)
{
unsigned char i;
SysTick_Config(72000); //定时1ms(HCLK = 72MHz)
KEY_Init();
LED_Init();
STM3210B_LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
USART2_Init(9600);
i2c_init();
for(i=0;i<16;i++)
pucBuf1[i] = i+0x30;
pucBuf1[14] = 0x0d;
pucBuf1[15] = 0x0a;
for(i=0;i<16;i++)
{
i2c_write(pucBuf1,i*16,16); //页写(最多16Byte)
LED_Disp(i);
LCD_DisplayChar(Line5,176,i/10+0x30);
LCD_DisplayChar(Line5,160,i%10+0x30);
printf("%02u",i);
}
i2c_read(pucBuf2,0,255);
printf("\r\n%s\r\n",pucBuf2);
while(1);
}
//SysTick 中断处理程序
void SysTick_Handler(void)
{
ulTick_ms++;
if(ulTick_ms % 1000 ==0)
ucSec++;
}
主函数分析:❤️ ❤️ ❤️
- 在使用各个外设前调用初始化函数,例如KEY、LED、LCD、USART2、i2c外设;
- 往AT24C02写入16*16个字节(即256个字节),然后读取AT24C02内容并保存到数组中,通过串口打印输出;
#ifndef __I2C_H__
#define __I2C_H__
#include "stm32f10x.h"
void i2c_init(void); //把IIC的SCL和SDA初始化为推挽输出
void delay1(unsigned int n); //软件粗略延时
void I2CStart(void); //IIC开始信号
void I2CStop(void); //IIC停止信号
void I2CSendAck(void); //主机发送应答信号
void I2CSendNotAck(void); //主机发送不应答信号
unsigned char I2CWaitAck(void); //等待从机应答
void I2CSendByte(unsigned char cSendByte); //通过IIC发送一个字节
unsigned char I2CReceiveByte(void); //通过IIC接收一个字节
void i2c_write(unsigned char* pucBuf,unsigned char ucAddr,
unsigned char ucNum); //往AT24C02某一个地址ucAddr写入ucNum个字节的数据pucBuf
void i2c_read(unsigned char* pucBuf,unsigned char ucAddr,
unsigned char ucNum); //获取AT24C02某一个地址ucAddr的ucNum个字节的数据pucBuf
#endif
简要分析:❤️ ❤️
- 底层的初始化引脚模式、I2C启动、I2C停止、I2C主机应答、I2C主机不应答;
- 中层的 I2C从机应答、I2C主机发送一个字节的数据、I2C主机接收一个字节的数据;
- 高层的给AT24C02某一个某一个地址ucAddr写入ucNum个字节的数据pucBuf、获取AT24C02某一个地址ucAddr的ucNum个字节的数据pucBuf;
#include "i2c.h"
/** I2C 总线接口 */
#define I2C_PORT GPIOB
#define SDA_Pin GPIO_Pin_7
#define SCL_Pin GPIO_Pin_6
#define FAILURE 0
#define SUCCESS 1
//配置SDA信号线为输入模式
void SDA_Input_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SDA_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}
//配置SDA信号线为输出模式
void SDA_Output_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SDA_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}
//
void SDA_Output( uint16_t val )
{
if ( val ) {
GPIO_SetBits(I2C_PORT,SDA_Pin);
} else {
GPIO_ResetBits(I2C_PORT,SDA_Pin);
}
}
//
void SCL_Output( uint16_t val )
{
if ( val ) {
GPIO_SetBits(I2C_PORT,SCL_Pin);
} else {
GPIO_ResetBits(I2C_PORT,SCL_Pin);
}
}
//
uint8_t SDA_Input()
{
return GPIO_ReadInputDataBit( I2C_PORT, SDA_Pin);
}
//延时程序
void delay1(unsigned int n)
{
unsigned int i;
for ( i=0;i<n;++i);
}
//I2C总线启动
void I2CStart(void)
{
SDA_Output(1);delay1(500);
SCL_Output(1);delay1(500);
SDA_Output(0);delay1(500);
SCL_Output(0);delay1(500);
}
//I2C总线停止
void I2CStop(void)
{
SCL_Output(0); delay1(500);
SDA_Output(0); delay1(500);
SCL_Output(1); delay1(500);
SDA_Output(1); delay1(500);
}
//等待应答
unsigned char I2CWaitAck(void)
{
unsigned short cErrTime = 5;
SDA_Input_Mode();
delay1(500);
SCL_Output(1);delay1(500);
while(SDA_Input())
{
cErrTime--;
delay1(500);
if (0 == cErrTime)
{
SDA_Output_Mode();
I2CStop();
return FAILURE;
}
}
//修改顺序
SCL_Output(0);delay1(500);
SDA_Output_Mode();
return SUCCESS;
}
//发送应答位
void I2CSendAck(void)
{
SDA_Output(0);delay1(500);
delay1(500);
SCL_Output(1); delay1(500);
SCL_Output(0); delay1(500);
}
//
void I2CSendNotAck(void)
{
SDA_Output(1);
delay1(500);
SCL_Output(1); delay1(500);
SCL_Output(0); delay1(500);
}
//通过I2C总线发送一个字节数据
void I2CSendByte(unsigned char cSendByte)
{
unsigned char i = 8;
while (i--)
{
SCL_Output(0);delay1(500);
SDA_Output(cSendByte & 0x80); delay1(500);
cSendByte += cSendByte; //左移一位
delay1(500);
SCL_Output(1);delay1(500);
}
SCL_Output(0);delay1(500);
}
//从I2C总线接收一个字节数据
unsigned char I2CReceiveByte(void)
{
unsigned char i = 8;
unsigned char cR_Byte = 0;
SDA_Input_Mode();
while (i--)
{
cR_Byte += cR_Byte; //左移一位
SCL_Output(0);delay1(500);
delay1(500);
SCL_Output(1);delay1(500);
cR_Byte |= SDA_Input();
}
SCL_Output(0);delay1(500);
SDA_Output_Mode();
return cR_Byte;
}
//I2C总线初始化
void i2c_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = SDA_Pin | SCL_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // **
GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}
//AT24C02
void i2c_read(unsigned char* pucBuf,unsigned char ucAddr,
unsigned char ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(ucAddr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
while(ucNum--)
{
*pucBuf++ = I2CReceiveByte();
if(ucNum)
I2CSendAck();
else
I2CSendNotAck();
}
I2CStop();
}
//AT24C02写
void i2c_write(unsigned char* pucBuf,unsigned char ucAddr,
unsigned char ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(ucAddr);
I2CWaitAck();
while(ucNum--)
{
I2CSendByte(*pucBuf++);
I2CWaitAck();
}
I2CStop();
delay1(500);
}
简要分析:❤️ ❤️
- 先把SCL和SDA初始化为推挽输出模式,同时编写SDA设置为推挽输出和下拉输入模式的初始化函数;
- 封装SCK和SDA输出高低电平的函数,还得编写SDA的接收高低电平,方便接收从机的接收信号;
- 根据I2C的通信原理编写开始、停止、应答等函数,IIC原理可参考嵌入式物联网常用的通信协议UART、RS-232、RS-422、RS-485、CAN、IIC、SPI;
- SCL时钟线和SDA信号线一般设置4us的延时,定义一个粗劣的延时就行,这个延时主要影响的是IIC通信的速率;
- 通过I2C总线发送一个字节数据I2CSendByte(unsigned char cSendByte);
- 先把SCL时钟线拉低,然后判断cSendByte字节的第8位与1进行&(与运算)等于1,如果是则SDA输出高电平1,否则输出低电平0;
- 然后左移一位判断cSendByte的第七位,SCL输出高电平,锁存SDA数据,发送SDA的数据;
- 重复以上步骤,直到把8位数据发送完。
- 注意:IIC发送时,字节数据的高位在前,低位在后!
- 从I2C总线接收一个字节数据unsigned char I2CReceiveByte(void);函数
- 定义两个char类型的变量,i记录接收的位数,cR_Byte接收IIC的每一位数据;
- 把SDA数据线设置为下拉输入模式,如果从机或主机不应答,SDA会接收到高电平;
- 轮询8次获取一个字节的数据,SCL先发送低电平0,等待大概8us应答信号,然后SCL输出高电平获取SDA的电平状态,并把数据保存到变量的cR_Byte中;
- 最后cR_Byte左移一位,继续重复以上步骤;
- AT24C02的接收驱动函数i2c_read(unsigned char* pucBuf,unsigned char ucAddr,unsigned char ucNum)
- 开始IIC通信,主机发送0xA0选择器件地址为A0的AT24C02存储器,等待从机应答;
- 然后给从机发送寻址地址ucAddr,等待从机应答;
- 再次开启IIC通信,主机发送0xA1读取从机数据地址的数据,等待从机应答;
- 主机获取从机发送的数据,并把ucNum个字节的数据保存指针变量中,如果没接收到ucNum个字节数据,则主机发送响应信号,否则发送不响应信号,最后IIC通信停止;
- AT24C02存储区的发送驱动函数i2c_write(unsigned char* pucBuf,unsigned char ucAddr,unsigned char ucNum)
- 开始IIC通信,主机发送0xA0选择器件地址为A0的AT24C02存储器,等待从机应答;
- 然后给从机发送寻址地址ucAddr,等待从机应答;
- 主机把ucNum个字节数据pucBuf发送给从机,每发一次数据等待从机响应,接收完成后,停止IIC通信;
- IIC的时钟线SCL和数据线SDA在拉高低电平时,应该设置4us的延时;
- 等待应答或者接收从机数据时,应把数据线SDA设置为下拉输入模式,接收完数据后把SDA设置为输出模式;
- 发送和接收完数据后,时钟线都拉低电平,防止数据混乱;
- ①根据IIC通信协议的原理,编写IIC的驱动代码,然后编写AT24C02应用函数,加深对IIC的理解与应用;
- ②掌握I2C的启动、停止、发送数据、响应所对应SCL、SDA高低电平的变换;
- ③学会主机和从机不应答,SDA会显示高电平;
- ④通过I2C协议,实现AT24C02数据存取 *** 作;
- ④数据左移一位等于 * 2,右移一位等于 / 2;
❤️ ❤️ ❤️ ❤️ ❤️ ❤️
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)