【学习笔记】--- STM32再学习.串行通信_IIC

一、IIC简介

1、IIC基本信息

通信标准 IIC
通信方式 同步通信
引脚说明 SCL:同步时钟;SDA数据输入/输出端
通信方向 半双工

IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。经常IIC和SPI接口被认为指定是一种硬件设备,但其实这样的说法是不尽准确的,严格的说他们都是人们所定义的软硬结合体,分为物理层(四线结构)协议层(主机,从机,时钟极性,时钟相位)
常见物理接口:
【学习笔记】--- STM32再学习.串行通信_IIC_第1张图片

2、IIC信号

信号 说明
开始信号(必要) SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号(可不要) SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号(可不要) 接收方接收到 8bit 数据后,向发送方发出特定的低电平脉冲(应答信号),表示已收到数据。若未收到应答信号,则判断通信失败。

具体如下:

起始与终止信号 【学习笔记】--- STM32再学习.串行通信_IIC_第2张图片
应答信号(以主发从收为例) 【学习笔记】--- STM32再学习.串行通信_IIC_第3张图片

参考 ---- [https://www.cnblogs.com/zhangjiansheng/p/7738390.html]

3、IIC数据传输

【学习笔记】--- STM32再学习.串行通信_IIC_第4张图片
①主发从收
在这里插入图片描述

②主收从发
在这里插入图片描述
注意:

  • IIC总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址;
  • 在通信前必须弄清主机与从机、发送方与接收方(这两组概念是不同的)。
  • IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定;只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。 (数据的有效性)

参考 1---- [https://blog.csdn.net/bluewhaletech/article/details/37876111]
参考2 ---- [https://www.cnblogs.com/aaronLinux/p/6218660.html]

二、IIC的使用(以stm32与24C02为例)

写在前面:IIC有根据历史原因分有硬件IIC与软件模拟IIC两种(有兴趣自己找找),以下的使用以软件模拟IIC为主(后序有机会再弄硬件IIC)。附两者区别:

是什么:

  • 模拟IIC:一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信波形;
  • 硬件IIC:对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用。

二者区别:

  • 硬件IIC用法比较复杂,模拟IIC的流程更清楚一些;
  • 硬件IIC速度比模拟快,并且可以用DMA;
  • 模拟IIC可以在任何管脚上,而硬件只能在固定管脚上;
  • 模拟I2C 是通过GPIO,软件模拟寄存器的工作方式,而硬件(固件)I2C是直接调用内部寄存器进行配置。

参考1 ---- [https://blog.csdn.net/faihung/article/details/59146937]
参考2 ---- [https://www.cnblogs.com/wy9264/p/11863239.html]

0、硬件介绍

24C02是什么:【百度百科-24C02】
STM32F1与24C02连接图:
【学习笔记】--- STM32再学习.串行通信_IIC_第5张图片
24C02引脚:

引脚名 说明 引脚名 说明
A0-A2 地址输入线 WP 写保护
SCL 时钟线 SDA 数据线

上面提到从机地址由7位组成,而地址输入线只有三位,因此完整的地址组成需看数据手册:
【学习笔记】--- STM32再学习.串行通信_IIC_第6张图片
前面的xK代表不同大小的24Cxx(型号)

1、程序部分

由于是使用软件模拟IIC,因此需要自己加入两个驱动程序,IIC驱动程序+从机设备(24C02)驱动程序,将两个驱动程序分别封装好加入项目即可。

1.1 IIC驱动程序部分

·宏定义(myiic.h)
·下列程序(myiic.c)
①IIC初始化
②产生 IIC 起始信号
③产生 IIC 停止信号
④等待应答信号到来(返回值:1->接收应答失败;0->接收应答成功)
⑤产生 ACK 应答(返回从机有无应答:1->有应答;0->无应答)
⑥不产生 ACK 应答(ack=1 时,发送 ACK;ack=0,发送 !ACK)
⑦发送一个字节
⑧读 1 个字节

·宏定义(myiic.h)

#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//IO方向设置
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函数	 
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA	 
#define READ_SDA   PBin(7)  //输入SDA 
//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号
#endif

·下列程序(myiic.c)
①IIC初始化

void IIC_Init(void)
{ 
RCC->APB2ENR|=1<<3; //先使能外设 IO PORTB 时钟
GPIOB->CRL&=0X00FFFFFF; //PB6/7 推挽输出
GPIOB->CRL|=0X33000000; 
GPIOB->ODR|=3<<6; //PB6,7 输出高
}

②产生 IIC 起始信号

void IIC_Start(void)
{
SDA_OUT(); //sda 线输出
IIC_SDA=1; 
IIC_SCL=1;
delay_us(4);
IIC_SDA=0; //START:when CLK is high,DATA change form high to low 
delay_us(4);
IIC_SCL=0; //钳住 I2C 总线,准备发送或接收数据
}

③产生 IIC 停止信号

void IIC_Stop(void)
{
SDA_OUT(); //sda 线输出
IIC_SCL=0;
IIC_SDA=0; //STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1; 
IIC_SDA=1; //发送 I2C 总线结束信号
delay_us(4); 
}

④等待应答信号到来(返回值:1->接收应答失败;0->接收应答成功)

u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA 设置为输入 
IIC_SDA=1;delay_us(1); 
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
} }
IIC_SCL=0;//时钟输出 0 
return 0; 
}

⑤产生 ACK 应答

void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}

⑥不产生 ACK 应答

void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}

⑦发送一个字节(返回从机有无应答:1->有应答;0->无应答)

void IIC_Send_Byte(u8 txd)
{ 
 u8 t; 
SDA_OUT(); 
 IIC_SCL=0;//拉低时钟开始数据传输
 for(t=0;t<8;t++)
 { 
 IIC_SDA=(txd&0x80)>>7;
 txd<<=1; 
delay_us(2); //对 TEA5767 这三个延时都是必须的
IIC_SCL=1;
delay_us(2); 
IIC_SCL=0;
delay_us(2);
 }
}

⑧读 1 个字节(ack=1 时,发送 ACK;ack=0,发送 nACK)

u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA 设置为输入
 for(i=0;i<8;i++ )
 {
 IIC_SCL=0; 
 delay_us(2);
 IIC_SCL=1;
 receive<<=1;
 if(READ_SDA)receive++; 
 delay_us(1); 
 }
if (!ack)
IIC_NAck();//发送 nACK
else
IIC_Ack(); //发送 ACK 
return receive;
}

未完待续

1.2 IIC驱动程序部分

·宏定义(24cxx.h)
·下列程序(24cxx.c)
①IIC接口初始化
②在指定地址读 1 个数据(返回值 :读到的数据)
③在指定地址写1 个数据(返回值 :读到的数据)
④检查从机设备(24C02)是否正常,假设使用其他
⑤在 AT24CXX 里面的指定地址开始连续读出指定个数的数据
⑥在 AT24CXX 里面的指定地址开始连续写入指定个数的数据

·宏定义(24cxx.h)

#ifndef __24CXX_H
#define __24CXX_H
#include "myiic.h" 
#define AT24C01 127
#define AT24C02 255
#define AT24C04 511
#define AT24C08 1023
#define AT24C16 2047
#define AT24C32 4095
#define AT24C64 8191
#define AT24C128 16383
#define AT24C256 32767 
//ALIENTEK STM32 开发板使用的是 24c02,所以定义 EE_TYPE 为 AT24C02
#define EE_TYPE AT24C02 
u8 AT24CXX_ReadOneByte(u16 ReadAddr);//指定地址读取一个字节
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); //指定地址写入一个字节
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);
//从指定地址开始写入指定长度的数据
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); 
//从指定地址开始读出指定长度的数据
u8 AT24CXX_Check(void); //检查器件
void AT24CXX_Init(void); //初始化 IIC
#endif

①IIC接口初始化

//初始化 IIC 接口
void AT24CXX_Init(void)
{
IIC_Init();
}

②在指定地址读 1 个数据(返回值 :读到的数据)

//ReadAddr:开始读数的地址
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{ 
u8 temp=0;
 IIC_Start(); 
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址 
}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));//发送器件地址 0XA0,写数据 
IIC_Wait_Ack(); 
 IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack(); 
IIC_Start(); 
IIC_Send_Byte(0XA1); //进入接收模式 
IIC_Wait_Ack();
 temp=IIC_Read_Byte(0); 
 IIC_Stop(); //产生一个停止条件 
return temp;
}

③在指定地址写1 个数据(返回值 :读到的数据)

//WriteAddr :写入数据的目的地址 
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
 IIC_Start(); 
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址 
}else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址 0XA0,写数据
IIC_Wait_Ack(); 
 IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack(); 
IIC_Send_Byte(DataToWrite); //发送字节 
IIC_Wait_Ack(); 
 IIC_Stop(); //产生一个停止条件
delay_ms(10); //EEPROM 的写入速度比较慢,加入延迟
}

④检查从机设备(24C02)是否正常,假设使用其他

u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(255);//避免每次开机都写 AT24CXX 
if(temp==0X55)return 0; 
else//排除第一次初始化的情况
{
AT24CXX_WriteOneByte(255,0X55);
 temp=AT24CXX_ReadOneByte(255); 
if(temp==0X55)return 0;
}
return 1; 
}

⑤在 AT24CXX 里面的指定地址开始连续读出指定个数的数据

//ReadAddr :开始读出的地址 对 24c02 为 0~255
//pBuffer :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--; }
}

⑥在 AT24CXX 里面的指定地址开始连续写入指定个数的数据

//WriteAddr :开始写入的地址 对 24c02 为 0~255
//pBuffer :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
while(NumToWrite--) {
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
} }

你可能感兴趣的:(【学习笔记】---,STM32再学习)