19_IIC读取AT24c02代码 STM32F407ZET

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
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值