1_IIC模块实现
2_全部代码
//IIC.h
#ifndef __I2C_H
#define __I2C_H
#include "sys.h" //段位映射
#define SCL PBout(8)
#define SDA_OUT PBout(9)
#define SDA_IN PBin(9)
void I2c_Init(void);
void AT24c02_Write_Byte(uint8_t addr,uint8_t Data);
uint8_t AT24c02_Read_Byte(uint8_t addr);
#endif
//sys.h
#ifndef __SYS_H_
#define __SYS_H_
#include "stm32f4xx.h"
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414
#define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814
#define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14
#define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014
#define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414 20 = 0x14
#define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814
#define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14
#define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014
#define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010 16 = 0x10
#define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410
#define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810
#define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10
#define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010
#define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410
#define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810
#define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10
#define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
#define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出
#define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入
#define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出
#define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入
#endif
//IIC.c
#include "stm32f4xx.h" // Device header
#include "IIC.h"
//IIC初始化
void I2c_Init(void){
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
//IIC切换模式
void I2c_Change_mode(GPIOMode_TypeDef Mode){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = Mode;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
//IIC启动信号
void IIC_Start(void){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//设置启动默认电平
SDA_OUT = 1;
SCL = 1;
Delay_us(5); //保持一定时间
//拉低数据线
SDA_OUT = 0;
Delay_us(5);
//拉低时钟线,以便使用下一次周期
SCL = 0;
}
//IIC停止电平
void IIC_Stop(void){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//设置默认电平
SDA_OUT = 0;
Delay_us(5);
//先拉高时钟电平,然后拉高数据电平
SCL = 1;
Delay_us(5);
SDA_OUT = 1;
}
//IIC发送一字节
void IIC_Send_Byte(uint8_t Data){
uint8_t i;
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
for(i=0;i<8;i++){
//数据交换
if(Data&0x01<<(7-i)){
SDA_OUT = 1;
}
else{
SDA_OUT = 0;
}
//延时,保持数据稳定
Delay_us(5);
//发送SDA数据
SCL = 1;
Delay_us(5);
SCL = 0;
}
}
//IIC读取一字节
uint8_t IIC_Read_Byte(void){
uint8_t i,Data=0;
//设置为输入模式
I2c_Change_mode(GPIO_Mode_IN);
for(i=0;i<8;i++){
Delay_us(5); //等待从机发送数据
SCL = 1; //从机发送数据到SDA
//接受到SDA的数据
if(SDA_IN){
Data |= 0x01<<(7-i);
}
//保持数据稳定
Delay_us(5);
//拉低SCL传输一次结束
SCL = 0;
}
return Data;
}
//主机等待应答
uint8_t IIC_Wait_ACK(void){
uint8_t ACK=0;
//设置为输入模式
I2c_Change_mode(GPIO_Mode_IN);
SCL = 1; //因为是从机发送数据,所以将控制权给从机
//等到输入电平,并判断 1 无应答 0 有应答
if(SDA_IN){
ACK = 1; //无应答
}
else{
ACK = 0; //有应答
}
Delay_us(5); //给从机的时间是5us
SCL = 0; //结束通信
return ACK;
}
//主句发送应答与非应答 1 非应答 0为应答
void IIC_Send_ACK(uint8_t ACK){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//发送应答(ACK)
if(ACK){
SDA_OUT = 1; //无应答
}
else{
SDA_OUT = 0; //有应答
}
SCL = 1; //发送一位的时候要先拉高,再拉低
Delay_us(5); //保持输出
SCL = 0; //拉低电平结束
}
void AT24c02_Write_Byte(uint8_t addr,uint8_t Data){
uint8_t ACK;
//IIC启动信号
IIC_Start();
//IIC发送设备地址
IIC_Send_Byte(0xa0);
//接收应答
ACK = IIC_Wait_ACK();
if(ACK){
printf("I2C 写入无应答 1");
return;
}
//发送存储地址
IIC_Send_Byte(addr);
//接收应答
ACK = IIC_Wait_ACK();
if(ACK){
printf("I2C 写入无应答 2");
return;
}
//发送数据
IIC_Send_Byte(Data);
//接受应答
ACK = IIC_Wait_ACK();
if(ACK){
printf("I2C 写入无应答 3");
return;
}
//发送停止
IIC_Stop();
}
uint8_t AT24c02_Read_Byte(uint8_t addr){
uint8_t ACK,Data;
//IIC启动信号
IIC_Start();
//IIC发送设备地址 7位地址 1位读写位 0为写 1为读
IIC_Send_Byte(0xa0);
//接受应答
ACK = IIC_Wait_ACK();
if(ACK){
printf("I2C 写入无应答 1_1");
return 0;
}
//发送要读的地址
IIC_Send_Byte(addr);
ACK = IIC_Wait_ACK();
if(ACK){
printf("I2C 写入无应答 2_1");
return 0;
}
//再次IIC启动
IIC_Start();
//再次寻找设备
IIC_Send_Byte(0xa1);
//等待应答
ACK = IIC_Wait_ACK();
if(ACK){
printf("I2C 读取无应答 1");
return 0;
}
//读取数据
Data = IIC_Read_Byte();
//发送非应答
IIC_Send_ACK(1);
//IIC停止
IIC_Stop();
//返回数据
return Data;
}
//delay.c
#include "stm32f4xx.h"
//168 000 000 1s 1000 1ms 1us 168
void Delay_us(unsigned int us){
SysTick->CTRL = 0;//Disable SysTick
SysTick->LOAD = 168*us - 1;
SysTick->VAL = 0;
SysTick->CTRL = 5; //使能systick时钟并启动定时器101
while((SysTick->CTRL & 0x00010000) == 0); //判断计数值是否为0 ,如果为0 则计时结束
SysTick->CTRL = 0; //关闭定时器
}
//1s 168000000 1ms 168000 1us 168
void Delay_ms(unsigned int ms){
while(ms--){
SysTick->CTRL = 0;//Disable SysTick
SysTick->LOAD = 168000 - 1;
SysTick->VAL = 0;
SysTick->CTRL = 5; //使能systick时钟并启动定时器
while((SysTick->CTRL & 0x00010000) == 0); //判断计数值是否为0 ,如果为0 则计时结束
SysTick->CTRL = 0; //关闭定时器
}
}
void Delay_s(unsigned int s){
while(s--){
Delay_ms(500);
Delay_ms(500);
}
}
//main.c
#include "stm32f4xx.h"
int main(void){
I2c_Init(); //IIC初始化
AT24c02_Write_Byte(0x12,0x58);
Delay_ms(100); //必须加延时,写入需要时间,不能还没有写入就去读取
printf("%#x\r\n",AT24c02_Read_Byte(0x12));
}
3_比较好理解代码
//6个好理解的代码模块
//IIC启动信号
void IIC_Start(void){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//设置启动默认电平
SDA_OUT = 1;
SCL = 1;
Delay_us(5); //保持一定时间
//拉低数据线
SDA_OUT = 0;
Delay_us(5);
//拉低时钟线,以便使用下一次周期
SCL = 0;
}
//IIC停止电平
void IIC_Stop(void){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//设置默认电平
//SCL = 0;
SDA_OUT = 0;
Delay_us(5);
//先拉高时钟电平,然后拉高数据电平
SCL = 1;
Delay_us(5);
SDA_OUT = 1;
}
void IIC_Send_Byte(uint8_t Data){
uint8_t i;
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
SCL = 0; //以便数据交换的时候时候是低电平,因为SCL为低电平,SDA才能改变电平
Delay_us(5); //为了保持稳定
for(i=0;i<8;i++){
//数据交换
if(Data&0x01<<(7-i)){
SDA_OUT = 1;
}
else{
SDA_OUT = 0;
}
//延时,保持数据稳定
Delay_us(5);
//发送SDA数据
SCL = 1;
Delay_us(5);
SCL = 0;
}
}
uint8_t IIC_Read_Byte(void){
uint8_t i,Data=0;
//设置为输入模式
I2c_Change_mode(GPIO_Mode_IN);
SCL = 0; //为了SDA数据交换
for(i=0;i<8;i++){
Delay_us(5); //等待从机发送数据
SCL = 1; //从机发送数据到SDA
//接受到SDA的数据
if(SDA_IN){
Data |= 0x01<<(7-i);
}
//保持数据稳定
Delay_us(5);
//拉低SCL传输一次结束
SCL = 0;
}
return Data;
}
uint8_t IIC_Wait_ACK(void){
uint8_t ACK=0;
//设置为输入模式
I2c_Change_mode(GPIO_Mode_IN);
SCL = 0; //以便数据交换
Delay_us(5); //保持电平稳定
SCL = 1; //因为是从机发送数据,所以将控制权给从机
//等到输入电平,并判断 1 无应答 0 有应答
if(SDA_IN){
ACK = 1; //无应答
}
else{
ACK = 0; //有应答
}
Delay_us(5); //给从机的时间是5us
SCL = 0; //结束通信
return ACK;
}
void IIC_Send_ACK(uint8_t ACK){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
SCL = 0; //拉低是为了数据交换
Delay_us(5); //为了稳定电平
//发送应答(ACK)
if(ACK){
SDA_OUT = 1; //无应答
}
else{
SDA_OUT = 0; //有应答
}
SCL = 1;
Delay_us(5); //保持输出
SCL = 0; //拉低电平结束
}
4_比较不好理解IIC代码
//IIC.c文件
//简化版,有点不好理解,记住,SCL电平在每个模块执行完之后都是低电平就好理解了
//停止信号除外
//IIC启动信号
//设置模拟引脚为 PB8 SCL PB9 SDA
#include "IIC.h"
#include "stm32f4xx.h"
//IIC初始化
void I2c_Init(void){
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
//IIC切换模式
void I2c_Change_mode(GPIOMode_TypeDef Mode){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = Mode;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
void IIC_Start(void){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//设置启动默认电平
SDA_OUT = 1;
SCL = 1;
Delay_us(5); //保持一定时间
//拉低数据线
SDA_OUT = 0;
Delay_us(5);
//拉低时钟线,以便使用下一次周期
SCL = 0;
}
//IIC停止电平
void IIC_Stop(void){
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
//设置默认电平
SDA_OUT = 0;
Delay_us(5);
//先拉高时钟电平,然后拉高数据电平
SCL = 1;
Delay_us(5);
SDA_OUT = 1;
}
//IIC发送一字节
void IIC_Send_Byte(uint8_t Data){
uint8_t i;
//设置为输出模式
I2c_Change_mode(GPIO_Mode_OUT);
for(i=0;i<8;i++){
//数据交换
if(Data&0x01<<(7-i)){
SDA_OUT = 1;
}
else{
SDA_OUT = 0;
}
//延时,保持数据稳定
Delay_us(5);
//发送SDA数据
SCL = 1;
Delay_us(5);
SCL = 0;
}
}
//IIC读取一字节
uint8_t IIC_Read_Byte(void){
uint8_t i,Data=0;
//设置为输入模式
I2c_Change_mode(GPIO_Mode_IN);
for(i=0;i<8;i++){
Delay_us(5); //等待从机发送数据
SCL = 1; //从机发送数据到SDA
//接受到SDA的数据
if(SDA_IN){
Data |= 0x01<<(7-i);
}
//保持数据稳定
Delay_us(5);
//拉低SCL传输一次结束
SCL = 0;
}
return Data;
}
//主机等待应答
uint8_t IIC_Wait_ACK(void){
uint8_t ACK=0;
//设置为输入模式
I2c_Change_mode(GPIO_Mode_IN);
SCL = 1; //因为是从机发送数据,所以将控制权给从机
//等到输入电平,并判断 1 无应答 0 有应答
if(SDA_IN){
ACK = 1; //无应答
}
else{
ACK = 0; //有应答
}
SCL = 1;
Delay_us(5); //给从机的时间是5us
SCL = 0; //结束通信
return ACK;
}
//IIC.h文件
#ifndef __I2C_H
#define __I2C_H
#include "sys.h" //段 位 映射头文件
#define SCL PBout(8)
#define SDA_OUT PBout(9)
#define SDA_IN PBin(9)
void I2c_Init(void);
void AT24c02_Write_Byte(uint8_t addr,uint8_t Data);
uint8_t AT24c02_Read_Byte(uint8_t addr);
#endif