首页 > 其他 > 详细

4. 软件模拟IIC通信(stm32为例)

时间:2020-09-05 23:21:52      阅读:190      评论:0      收藏:0      [点我收藏+]

1. 硬件连接

1.1 从设备端引脚连接

技术分享图片

1.2 mcu端引脚连接

技术分享图片

2. iic初始化

 1 #include "delay.h"
 2 
 3 #define IIC_SCL    PBout(8) //SCL(输出)
 4 #define IIC_SDA    PBout(9) //SDA(输出)   
 5 #define IIC_SDA_R  PBin(9)  //SDA(输入)
 6 
 7 void AT24C0X_IIC_Init(void)
 8 {           
 9   GPIO_InitTypeDef  GPIO_InitStructure;
10 
11   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能gpiob时钟
12   //GPIOB8,B9初始化
13   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
14   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //普通输出模式
15   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出,增加输出电流能力
16   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz,高速响应
17   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  //使能上拉电阻
18   GPIO_Init(GPIOB, &GPIO_InitStructure);        //初始化gpio
19   PBout(8)=1;       // iic_scl,时序图初始为高电平
20   PBout(9)=1;       // iic_sda,时序图初始为高电平
21 }
22 void sda_pin_mode(GPIOMode_TypeDef mode)    // GPIO_Mode_IN,GPIO_Mode_OUT
23 {
24   GPIO_InitTypeDef  GPIO_InitStructure;
25 
26   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能gpiob时钟
27   //GPIOB8,B9初始化
28   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
29   GPIO_InitStructure.GPIO_Mode = mode;  //普通输入或出模式
30   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出,增加输出电流能力
31   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz,高速响应
32   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  //使能上拉电阻
33   GPIO_Init(GPIOB, &GPIO_InitStructure);        //初始化gpio
34 }

3.时序图

3.1 开始和终止的定义(Start and Stop Definition)

 技术分享图片

3.1.1 开始信号

 1 void iic_start(void)
 2 {
 3     //保证sda引脚为输出模式
 4     sda_pin_mode(GPIO_Mode_OUT);
 5 
 6     IIC_SCL = 1;    // 1.初始电平都为高电平
 7     IIC_SDA = 1;
 8     delay_us(5);    //   延时5us,保证电平稳定有效
 9 
10     IIC_SDA = 0;    // 2.scl为高期间,sda:1->0
11     delay_us(5);    //   产生start信号
12 
13     IIC_SCL = 0;    // 3.钳住iic总线,准备开始发送数据
14     delay_us(5);    //
15 }

3.1.2 终止信号(停止信号)

void iic_stop(void)
{
    //保证sda引脚为输出模式
    sda_pin_mode(GPIO_Mode_OUT);

    IIC_SCL 1;    // 1.初始电平为高电平
    IIC_SDA = 0;    //   初始电平为低电平
    delay_us(5);

    IIC_SDA 1;    // 2.scl为高期间,sda:0->1
    delay_us(5);
}

3.2 输出应答(Data Validity)

 技术分享图片

3.2.1 从机应答(主机等待从机应答)

 1 uint8_t iic_wait_ack(void)
 2 {
 3     uint8_t ack = 0;
 4 
 5     //保证sda引脚为输出模式
 6     sda_pin_mode(GPIO_Mode_IN);
 7     IIC_SCL = 1;        // 在scl=1期间,读取sda
 8     delay_us(5);
 9 
10     if(IIC_SDA_R)
11     {
12         ack = 1;        // 若sda_r为1,则无应答
13     }
14     else
15     {
16         ack = 0;        // 若sda_r为0,则有应答
17     }
18     return ack;
19 }

3.2.2 主机应答

void iic_ack(void)
{
    //保证sda引脚为输出模式
    sda_pin_mode(GPIO_Mode_OUT);

    IIC_SCL 0;    // scl初始电平
    IIC_SDA = 0;    // sda初始电平
    delay_us(5);

    IIC_SCL 1;    // scl=1期间,
    delay_us(5);    

    IIC_SDA 0;    // sda=0,主机应答
}

3.2.3 主机不应答

 1 void iic_no_ack(void)
 2 {
 3     //保证sda引脚为输出模式
 4     sda_pin_mode(GPIO_Mode_OUT);
 5 
 6     IIC_SCL = 0;    // scl初始电平
 7     IIC_SDA = 1;    // sda初始电平
 8     delay_us(5);
 9 
10     IIC_SCL = 1;    // scl=1期间,
11     delay_us(5);
12 
13     IIC_SDA = 1;    // sda=1,主机不应答
14 }

3.3 读写时序

 

3.3.1 数据有效性(Data Validity

技术分享图片

 

3.3.2 设备地址(Device Address

 技术分享图片

3.3.3 写操作

 1 void iic_write_byte(uint8_t byte)
 2 {
 3     int32_t i;
 4     //保证sda引脚为输出模式
 5     sda_pin_mode(GPIO_Mode_OUT);
 6 
 7     IIC_SCL = 0;
 8     delay_us(5);
 9 
10     for (i=7; i<0; i--)
11     {
12         if (byte & (1<<i))  // 只有scl=1时才能写数据,bit流由高到低
13         {
14             IIC_SDA = 1;    
15         }
16         else
17         {
18             IIC_SDA = 0;
19         }
20         delay_us(5);
21 
22         IIC_SCL = 1;        // 
23         delay_us(5);
24 
25         IIC_SCL = 0;        // 为下一次写数据做准备
26         delay_us(5);
27     }
28 }

3.3.4 读操作

 1 uint8_t iic_read_byte(void)
 2 {
 3     uint8_t byte;
 4     int32_t i;
 5 
 6     //保证sda引脚为输出模式
 7     sda_pin_mode(GPIO_Mode_IN);
 8 
 9     IIC_SCL = 0;            // 初始化为低电平
10     delay_us(5);    
11 
12     for (i=7; i>0; i--)
13     {
14         IIC_SCL = 1;        // 只有在scl=1时,才可以读数据
15         delay_us(5);
16 
17         if (IIC_SDA_R)
18         {
19             byte |= 1<<i;
20         }
21         else
22         {
23             byte |= 0<<i;
24         }
25         IIC_SCL = 0;        // 为都下一个字节的数据做准备
26         delay_us(5);    
27     }
28     return byte;
29 }

3.4 向AT24CXX写数据

3.4.1 写一字节数据( Byte Write

 技术分享图片

 1 uint32_t AT24CXX_Byte_Write(uint8_t addr,uint8_t byte)
 2 {
 3     uint8_t ack;
 4 
 5     // 1.发送开始信号
 6     iic_start();
 7 
 8     // 2.写设备地址,选择iic总线的设备,bit流由高到低(从最高位开始写)
 9     iic_write_byte(0xA0);
10 
11     // 3.等待从机应答(从机位EEPROM设备,主机位mcu设备)
12     ack = iic_wait_ack();
13     if (1 == ack)
14     {
15         printf("dev addr is error!\r\n");
16         return -1;
17     }
18 
19     // 4.写入数据地址(数据在eeprom中要存放的位置)
20     iic_write_byte(addr);
21 
22     // 5.等待从机应答
23     ack = iic_wait_ack();
24     if (1 == ack)
25     {
26         printf("data addr is error!\r\n");
27         return -2;
28     }
29 
30     // 6.写入要存入EEPROM中的数据
31     iic_write_byte(byte);
32 
33     // 7.等到从机应答
34     ack = iic_wait_ack();
35     if (1 == ack)
36     {
37         printf("data addr is error!\r\n");
38         return -3;
39     }
40 
41     // 8.发送终止信号
42     iic_stop();
43 
44     return 0;
45 }

3.4.2 写一页的数据(Page Write

 技术分享图片

 1 int32_t AT24C0X_Page_Write(uint8_t addr,uint8_t *pbuf,uint8_t len)  
 2 {
 3     uint8_t ack;
 4     uint8_t i;
 5 
 6     // 1.发送起始信号
 7     iic_start();
 8 
 9     // 2.发送设备地址,iic总线寻址
10     iic_write_byte(0xA0);   // 写设备地址(0xA0)
11     
12     // 3.等待应答信号
13     ack = iic_wait_ack();
14     if (ack)
15     {
16         printf("dev addr is err!\r\n");
17         return -1;
18     }
19 
20     // 4.发送要写入的数据地址
21     iic_write_byte(addr);
22 
23     // 5.等待应答信号
24     ack = iic_wait_ack();
25     if (ack)
26     {
27         printf("data addr is err!\r\n");
28         return -2;
29     }
30 
31     // 6.发送要写入的数据(多字节)
32     for (i=0; i<len; i++)
33     {
34         // 发送要写入的数据
35         iic_write_byte(pbuf[i]);
36         // 等待应答
37         ack = iic_wait_ack();
38         if (ack)
39         {
40             printf("data is err!\r\n");
41             return -3;
42         }               
43     }
44     // 7.发送停止信号
45     iic_stop();
46 }

3.4.3 写入多字节的数据(写入数据流)

 1 int32_t AT24CXX_Write_Data(uint8_t Addr,uint8_t *pBuf,uint16_t Len) 
 2 {
 3     while(Len--)
 4     {
 5         AT24CXX_Byte_Write(Addr,pBuf);
 6         Addr++;         // 目的地址(eeprom中存储数据的地址+1)
 7         pBuf++;         // 源地址(源数据的地址)
 8     }
 9     return 0;
10 }

3.5 从AT24CXX读数据

3.5.1 任意地址读一个byteRandom Read

 技术分享图片

 1 uint8_t AT24CXX_Read_Byte_Random(uint8_t addr)
 2 {
 3     uint8_t byte;
 4     uint8_t ack;
 5 
 6     // 1.发送开始信号
 7     iic_start();
 8 
 9     // 2.写入要读的设备地址(硬件设备id),为写方向
10     iic_write_byte(0xA0);
11 
12     // 3.等待从机应答信号
13     ack = iic_wait_ack();
14     if (ack)
15     {
16         printf("When Master read randomly,what write slave device address is error! r\n");
17         return -1;
18     }
19 
20     // 4.写入从机设备(eeprom)中,要读的位置
21     iic_write_byte(addr);
22 
23     // 5.等待从机应答信号
24     ack = iic_wait_ack();
25     if (ack)
26     {
27         printf("When Master read randomly,what write data address in slave is error! r\n");
28         return -2;
29     }
30 
31     // 6.发送开始信号
32     iic_start();
33 
34     // 7.写入要读的设备地址(硬件设备id),为读方向
35     iic_write_byte(0xA1);
36 
37     // 8.等待从机应答信号
38     ack = iic_wait_ack();
39     if (ack)
40     {
41         printf("When Master read randomly,what write data address in slave is error! r\n");
42         return -2;
43     }
44 
45     // 9.读取从机数据(eeprom)
46     byte = iic_read_byte();
47 
48     // 10.主机不应答
49     iic_no_ack();
50 
51     // 11.发送停止信号
52     iic_stop();
53 
54     return byte;
55 }

3.5.2 读出shortint型的数据

 1 /**
 2   * @brief 在AT24XX里面的指定地址开始读出长度为Len的数据,该函数用于读出
 3   *        读出16bit或者32bit的数据    
 4   * @param ReadAddr:开始读出的地址
 5   * @param Len:  读出数据的长度2,4
 6   *
 7   * @retval          数据             
 8   */
 9 uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len)
10 {   
11     uint8_t t;
12     uint32_t temp=0;
13     for(t=0;t<Len;t++)
14     {
15         temp<<=8;
16         temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);    //                     
17     }
18     return temp;                                                    
19 }

3.5.3 读出epprom中一定长度的数据

 1 /**
 2   * @brief 在AT24XX里面的指定地址开始读出指定长度为Len的数据
 3   * @param ReadAddr:开始读出的地址,对24c02为0-255
 4   * @param pData:   输出数据数组首地址   
 5   * @param Len:     要读数据的个数
 6         
 7   */
 8 void AT24CXX_Read_Data(uint16_t addr,uint8_t *pData, uint16_t DataLen)
 9 {
10     while (DataLen--)
11     {
12         *pData++ = AT24CXX_Read_Byte_Random(addr++);
13     }
14 }

 

4. 软件模拟IIC通信(stm32为例)

原文:https://www.cnblogs.com/sbtblogs/p/13619303.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!