zoukankan      html  css  js  c++  java
  • IIC

    一,概述

    二,信号概念

                          

    三,24C02

    1.地址

                  

    2,写数据是时序图

    3,读数据时时序图

    #include <stdio.h>
    #include "stm32f4xx.h"
    #include "sys.h"
    #include "string.h"
    
    static GPIO_InitTypeDef   GPIO_InitStructure;
    
    static NVIC_InitTypeDef   NVIC_InitStructure;
    
    static USART_InitTypeDef  USART_InitStructure;
    
    
    
    #define SCL            PBout(8)
    #define SDA_W        PBout(9)
    #define SDA_R        PBin(9)
    
    
    //重定义fputc函数 
    int fputc(int ch, FILE *f)
    {     
        USART_SendData(USART1,ch);
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  
        
        return ch;
    }   
    
    void delay_us(uint32_t nus)
    {        
        uint32_t temp;             
        SysTick->LOAD =SystemCoreClock/8/1000000*nus;     //时间加载               
        SysTick->VAL  =0x00;                            //清空计数器
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;         //使能滴答定时器开始倒数      
        do
        {
            temp=SysTick->CTRL;
        }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
        SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;         //关闭计数器
        SysTick->VAL =0X00;                               //清空计数器 
    }
    
    void delay_ms(uint16_t nms)
    {                     
        uint32_t temp;           
        SysTick->LOAD=SystemCoreClock/8/1000*nms;        //时间加载(SysTick->LOAD为24bit)
        SysTick->VAL =0x00;                               //清空计数器
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //能滴答定时器开始倒数 
        do
        {
            temp=SysTick->CTRL;
        }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
        SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
        SysTick->VAL =0X00;                               //清空计数器              
    } 
    
    void USART1_Init(uint32_t baud)
    {
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);                             //使能GPIOA时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);                            //使能USART1时钟
     
        //串口1对应引脚复用映射
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);                         //GPIOA9复用为USART1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);                         //GPIOA10复用为USART1
        
        //USART1端口配置
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;                         //GPIOA9与GPIOA10
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                    //复用功能
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                //速度50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                     //推挽复用输出
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                                     //上拉
        GPIO_Init(GPIOA,&GPIO_InitStructure);                                             //初始化PA9,PA10
    
        //USART1 初始化设置
        USART_InitStructure.USART_BaudRate = baud;                                        //波特率设置
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;                        //字长为8位数据格式
        USART_InitStructure.USART_StopBits = USART_StopBits_1;                            //一个停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;                                //无奇偶校验位
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    //无硬件数据流控制
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                    //收发模式
        USART_Init(USART1, &USART_InitStructure);                                         //初始化串口
        
        USART_Cmd(USART1, ENABLE);                                                      //使能串口1 
        
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                    //开启相关中断
    
        //Usart1 NVIC 配置
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;                                //串口1中断通道
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;                            //抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;                                //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                    //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure);                                                    //根据指定的参数初始化VIC寄存器
    }
    
    
    void i2c_init(void)
    {
    
        //设置PB8与PB9引脚为输出模式
        /*!< Enable GPIO clocks ,使能GPIOB的硬件时钟*/
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
        
        
        /* 配置片选引脚PB8 PB9为输出模式*/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(GPIOB, &GPIO_InitStructure);    
        
        //设置SCL和SDA引脚为高电平
        SCL=1;
        SDA_W=1;
    }
    
    //用于方便切换sda引脚的输入和输出模式
    void i2c_sda_mode(uint32_t mode)
    {
        /* 配置片选引脚PB9为输入模式*/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = mode;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(GPIOB, &GPIO_InitStructure);    
    }
    
    void i2c_start(void)
    {
        //sda引脚为输出模式
        i2c_sda_mode(GPIO_Mode_OUT);
        
        //SCL与SDA引脚默认是高电平
        SCL=1;
        SDA_W=1;
        
        //延时5us
        delay_us(5);
        
        
        //SDA输出低电平
        SDA_W=0;
        
        //延时5us
        delay_us(5);
        
    
        //SCL引脚输出低电平
        SCL=0;
    
    
    }
    
    
    void i2c_stop(void)
    {
        //sda引脚为输出模式
        i2c_sda_mode(GPIO_Mode_OUT);
        
        //SCL与SDA引脚默认是低电平
        SCL=0;
        SDA_W=0;
        
        
        //延时5us
        delay_us(5);
        
        //SCL输出高电平
        SCL=1;
        
        
        //延时5us
        delay_us(5);
    
        //SDA输出高电平    
        SDA_W=1;
        
        //延时5us
        delay_us(5);    
    }
    
    
    void i2c_send_byte(uint8_t txd)
    {
        uint32_t i=0;
        
        //sda引脚为输出模式
        i2c_sda_mode(GPIO_Mode_OUT);
        
        //保证SCL引脚为低电平,允许SDA引脚电平发生改变
        SCL=0;
        
        //延时5us
        delay_us(5);
        
        //发送txd这个参数,分8个bit进行发送,MSB格式
        
        for(i=0; i<8; i++)
        {
            //MSB,最高有效位发送数据
            if(txd & (1<<(7-i)))
                SDA_W =1;
            else
                SDA_W =0;
            
            //因为SDA线比SCL引脚电平提前变化,需要延时
            delay_us(5);
            
            //设置SCL高电平
            SCL=1;
            delay_us(5);
        
            //设置SCL低电平
            SCL=0;
            delay_us(5);
        
        }    
        
    }
    uint8_t i2c_recv_byte(void)
    {
        uint32_t i=0;
        uint8_t  rxd=0;
        
        //保证SDA引脚为输出模式
        i2c_sda_mode(GPIO_Mode_IN);
    
        //保证SCL引脚开始的时候为低电平,允许数据的改变
        SCL =0;
        delay_us(5);
        
        //连续接收8个bit,采用最高有效位优先进行接收
        for(i=0; i<8; i++)
        {
    
            
            delay_us(5);
            
            //锁存数据
            SCL=1;
            delay_us(5);
            
            if(SDA_R)
                rxd|=1<<(7-i);        
            
            //允许改变数据
            SCL=0;
            delay_us(5);        
        
        }
        
        return rxd;
    
    }
    
    
    void i2c_ack(uint8_t ack)
    {
    
        //保证SDA引脚为输出模式
        i2c_sda_mode(GPIO_Mode_OUT);
    
        //保证SCL引脚开始的时候为低电平,允许数据的改变
        SCL =0;
        delay_us(5);
        
    
        if(ack)
            SDA_W=1;
        else
            SDA_W=0;
        
        delay_us(5);
        
        //锁存数据,让从机进行识别
        SCL=1;
        delay_us(5);
        
        //允许改变数据,从机无视该数据
        SCL=0;
        delay_us(5);        
    
    }
    
    uint8_t i2c_wait_ack(void)
    {
        uint8_t ack=0;
    
    
        //sda引脚为输入模式
        i2c_sda_mode(GPIO_Mode_IN);
        
        
        //SCL输出高电平
        SCL=1;
        
        
        //延时5us
        delay_us(5);
        
        //判断SDA引脚的电平,如果为1,就返回1,就是无应答;若为0,就返回0,意味着是有应答
        if(SDA_R)
        {
            
            //调用该函数,将SCL和SDA引脚恢复到原来高电平的状态
            i2c_stop();
            
            ack=1;
        
        }
        else    
            ack=0;
        
        //SCL输出低电平,保持占用i2c总线
        SCL=0;
        
        //延时5us
        delay_us(5);
        
        return ack;
    
    }
    
    
    
    void at24c02_write(uint8_t addr,uint8_t *pbuf,uint32_t len)
    {
        uint8_t ack=0;
        
        //发送起始信号
        i2c_start();
        
        //发送寻址地址0xA0
        i2c_send_byte(0xA0);
        
        //等待应答
        ack=i2c_wait_ack();
        
        if(ack)
        {
        
            printf("24c02 ack device address fail
    ");
            
            return ;
        }
        
        printf("24c02 is online
    ");
        
        
        
        //发送数据存储地址
        i2c_send_byte(addr);
        
        //等待应答
        ack=i2c_wait_ack();
        
        if(ack)
        {
        
            printf("24c02 ack word address fail
    ");
            
            return ;
        }
        
        printf("24c02 word address ok
    ");
        
        //发送数据内容
        while(len--)
        {
            //发送数据内容
            i2c_send_byte(*pbuf++);
            
            //等待应答
            ack=i2c_wait_ack();
            
            if(ack)
            {
            
                printf("24c02 ack data fail
    ");
                
                return ;
            }        
        
        
        }
        
        //发送停止信号
        i2c_stop();
    }
    void at24c02_read(uint8_t addr,uint8_t *pbuf,uint8_t len)
    {
        uint8_t ack=0;
        
        //发送启动信号
        i2c_start();
        
        //发送寻址地址为0xA0,写访问操作
        i2c_send_byte(0xA0);
        
        //等待应答
        ack = i2c_wait_ack();
        
        if(ack)
        {
            printf("24c02 ack device address fail
    ");
            
            return;
        
        }
    
        printf("24c02 is online
    ");
    
        //发送数据存储地址
        i2c_send_byte(addr);
        
        //等待应答
        ack = i2c_wait_ack();
        
        if(ack)
        {
            printf("24c02 ack word address 1 fail
    ");
            
            return;
        
        }    
        
        printf("24c02 word address ok
    ");    
        
        //重新发送启动信号
        i2c_start();
        
        //发送寻址地址为0xA1,读访问操作
        i2c_send_byte(0xA1);
        
        //等待应答
        ack = i2c_wait_ack();
        
        if(ack)
        {
            printf("24c02 ack device address 2 fail
    ");
            
            return;
        
        }
        
        len=len-1;
        
        while(len--)
        {
            //接收数据
            *pbuf++=i2c_recv_byte();
            
            //主动发送应答给从机
            i2c_ack(0);
        }
        
        //接收数据
        *pbuf=i2c_recv_byte();
        
        //主动发送无应答给从机
        i2c_ack(1);    
        
        //发送停止信号,整个通信过程结束
        i2c_stop();
        
        printf("24c02 read ok
    ");
    
    }
    
    int main(void)
    {
        uint16_t w25qxx_id=0;
        uint8_t buf_wr[64]={0};
        uint8_t buf_rd[64]={0};    
        uint32_t i=0;
    
        //系统定时器初始化,时钟源来自HCLK,且进行8分频,
        //系统定时器时钟频率=168MHz/8=21MHz
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
            
        //设置中断优先级分组2
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        
        //串口1,波特率115200bps,开启接收中断
        USART1_Init(115200);
    
        i2c_init();
        
        //写入数据
        memset(buf_wr,1,sizeof buf_wr);
        printf("at24c02_write all is 1
    ");
        at24c02_write(0,buf_wr,8);
        
        //读写之间必须得加延时,如果写完之后立即读取就会产生读取失败
        delay_ms(500);
        
        //读取数据
        memset(buf_rd,0,sizeof buf_rd);
        
        
        printf("at24c02_read
    ");
        at24c02_read(0,buf_rd,8);
    
        for(i=0; i<8; i++)
        {
            printf("%02X ",buf_rd[i]);
        
        }
        
        printf("
    ");
        
        
        while(1)
        {
            
    
        }
    
    }
    main.c

    思考题1:OLED屏的设备地址是多少?

    回答:设备地址为0x78.

    思考题2:在24c02控制时钟的时候,为什么要进行5us的延时,小于5us延时或许大于5us的延时是否可以,示例代码如下:

    //设置SCL高电平
    SCL=1;
    delay_us(5);
        
    //设置SCL低电平
    SCL=0;
    delay_us(5);

    回答:大于5us是可以的,如果使用5ms也是可以的。但是延时是不能低于1.2us,详细描述如下图。

  • 相关阅读:
    HTML学习(4)属性
    HTML学习(3)基础
    HTML学习(2)编辑器
    HTML学习(1)简介
    winform学习(10)设置控件透明背景色
    winform学习(9)无边框拖动窗体
    《分布式机器学习:算法、理论与实践》——【RE5】
    《分布式机器学习:算法、理论与实践》——【RE4】
    《机器学习在搜索广告中的机遇和挑战》
    >>《learning to rank(ltr)
  • 原文地址:https://www.cnblogs.com/xiangtingshen/p/10941237.html
Copyright © 2011-2022 走看看