实验目的
本实验向大家介绍如何使用STM32的IIC硬件接口,实现和24C02之间的双向通信,通过本实验的学习,我们将对IIC通信的过程会有一个详细的了解。
实验简介
IIC简介
IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及外围设备。它是由数据线SDA和时钟线SCL构成的串行总线,可用于发送和接收数据。在CPU与被控IC之间,IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上,但是目前大多数IIC设备不支持高速IIC。
使用IIC总线通信时,只需要2根通信线,一条双向串行数据线(SDA),一条串行时钟线(SCL)。每个IIC设备都有一个独立的地址,主机可以利用这个地址进行不同设备间的访问。当多主机使用总线时,IIC有一个仲裁机制,会决定由那个主机使用总线。
IIC总线协定如下:
- 只有在总线空闲时才允许启动数据发送。
- 在数据传送过程中,当时钟线为高电平时,数据线必须保持稳定状态,不允许有跳变。时钟线为高电平时,数据线的任何电平变化将被看作总线的起始或停止信号。
IIC在数据通信过程中共有3种信号,分别是起始信号,停止信号和应答信号。
**起始信号:**时钟线(SCL)保持高电平期间,数据线(SDA)电平从高到低跳变作为I2C总线的起始信号。
**停止信号:**时钟线(SCL)保持高电平期间,数据线(SDA)电平从低到高的跳变作为I2C总线的停止信号
**应答信号:**接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,则判断为受控单元出现故障。
器件简介
本实验所用IIC器件为非易失性存储器EEPROM——AT24C02,容量为256字节,可擦写100万次,掉电数据不会丢失,通常被用来保存一些用户数据。这个系列芯片容量可以达到1Mbit,即128K字节,如AT24C1024.
AT24C02的管脚配置如下,关于这个芯片详细介绍请参看数据手册:
电路设计
星光STM32F103开发板采用的EEPROM芯片为AT24C02,容量为256字节,这里我们把A0-A2均接地,也就是将AT24C02的地址设为0,写程序时要注意这点,连接到STM32的I2C2,对应管脚PB10,PB11,电路如图所示
代码
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #include "MyIncludes.h" u16 sys_cnt = 0; void systick_isr(void) { if(sys_cnt < 1000) { sys_cnt++; } else { sys_cnt = 0; HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5); } } u8 Wr_Buf[100]; //写缓冲器 u8 Rd_Buf[100]; //读缓冲器 int main() { u16 i; System_Init(); LED_Init(); SysTick_Init(systick_isr); USART1_Init(115200,NULL,NULL); printf("EEPROM ReadWrite Test\n"); while(EEPROM_Init()!=EEPROM_OK) { printf("EEPROM Init ERROM\n"); } //当EEPROM初始化未完成 //输出EEPROM Init ERROM for(i=0; i<100; i++) { Wr_Buf[i] = i+1; } EEPROM_Write(Wr_Buf,0,100); EEPROM_Read(Rd_Buf,0,100); if(strncmp((char *)Wr_Buf,(char *)Rd_Buf,100) == 0) { printf("EEPROM TEST OK\n"); } else { printf("EEPROM TEST ERROR"); } while(1) { } } |
24cxx.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #ifndef __24CXX_H_ #define __24CXX_H_ #include "stm32f1xx.h" #include "stm32_types.h" #include "stm32f1xx_hal.h" #define EEPROM_MAX_TRIALS ((uint32_t)3000) //函数的最大试用次数(貌似未用到) #define EEPROM_OK ((uint8_t)0) //状态:成功 #define EEPROM_FAIL ((uint8_t)1) #define EEPROM_TIMEOUT ((uint8_t)2) #define EEPROM_I2C_ADDRESS ((uint16_t)0xA0) //EEPROM地址 0xA0 #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 uint8_t EEPROM_Init(void); //EEPROM初始化 uint32_t EEPROM_Read(uint8_t* pBuffer,uint16_t ReadAddr,uint16_t NumToRead); //指定位置读出指定个数的数据(数据数组首地址,开 //始读出的地址,要读出数据的个数); uint32_t EEPROM_Write(uint8_t* pBuffer,uint16_t WriteAddr,uint16_t NumToWrite); //在指定位置写入指定个数的数据,(数据数组首地址 //开始写入地址,要写入数据个数) void EEPROM_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len); //在指定地址写入长度为Len的数据(开始写入的地址 //,数据数组首地址,要写入数据的长度) uint32_t EEPROM_ReadLenByte(u16 ReadAddr,u8 Len); //在指定地址读出长度为Len的数据(开始读出的地址 //要读的长度) #endif |
24cxx.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | #include "24cxx.h" #include "iic.h" I2C_HandleTypeDef hI2cEEpromHandler; void EEPROM_IO_Init(void) //EEPROM的I2c接口初始化 { I2C2_Init(&hI2cEEpromHandler); } HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress,uint16_t MemAddress,uint8_t* pBuffer,uint32_t BufferSize) //向EEPROM写入数据(器件地址,器件寄存器地址,代写入数据,写入长度) { return (I2C2_WriteMultiple(&hI2cEEpromHandler,DevAddress,MemAddress,I2C_MEMADD_SIZE_8BIT,pBuffer,BufferSize)); //I2C写操作(I2chandler,器件地址,器件寄存器地址 //一次发送数据大小,待写入的数据,写入长度) } HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress,uint16_t MemAddress,uint8_t* pBuffer,uint32_t BufferSize) //从EEPROM读取数据(器件地址,器件寄存器地址,待 //读取数据,待读取长度) { return (I2C2_ReadMultiple(&hI2cEEpromHandler,DevAddress,MemAddress,I2C_MEMADD_SIZE_8BIT,pBuffer,BufferSize)); //I2C读操作(i2c handler,器件地址,器件寄存 //器地址,一次发送数据大小8位,待写入数据,长度) } HAL_StatusTypeDef EEPROM_IO_IsDeviceReady(uint16_t DevAddress,uint32_t Trials) //检查EEPROM是否准备就绪通信(器件地址,检测最大次数) { return(I2C2_IsDeviceReady(&hI2cEEpromHandler,DevAddress,Trials)); //判断器件是否就绪(I2C Hanler,器件地址 //检测最大次数) } __IO uint16_t EEPROMAddress = 0; //EEPROMAddress 地址声明 uint8_t EEPROM_Init(void) { EEPROM_IO_Init(); //初始化I2C总线 EEPROMAddress = EEPROM_I2C_ADDRESS; //EEPROMAddress = 0xA0 if(EEPROM_IO_IsDeviceReady(EEPROMAddress,EEPROM_MAX_TRIALS) != HAL_OK) //检查EEPROM是否准备就绪通信 { return EEPROM_FAIL; //返回失败 } return EEPROM_OK; //返回成功 } uint32_t EEPROM_WaitEepromStandbyState(void) //等待EEPROM就绪 { if(EEPROM_IO_IsDeviceReady(EEPROMAddress,EEPROM_MAX_TRIALS) != HAL_OK) //检查EEPROM是否准备就绪(器件地址,检测最大次数) { return EEPROM_TIMEOUT; } return EEPROM_OK; } //实验未用到 uint8_t EEPROM_ReadOneByte(uint16_t ReadAddr) //在AT24C02中读出指定个个数数据 { uint8_t ReadVal = 0; if(EEPROM_IO_ReadData(EEPROMAddress,ReadAddr,&ReadVal,1) != HAL_OK) //从EEPROM中读取数据(器件地址,开始读取地址,待写入的数据,写入长度) { ReadVal = 0xFF; } return ReadVal; } uint32_t EEPROM_Read(uint8_t* pBuffer,uint16_t ReadAddr,uint16_t NumToRead) //EEPROM读数据 缓冲区首地址,要读的地址,要读的个数 { uint32_t buffersize = NumToRead; if(EEPROM_IO_ReadData(EEPROMAddress,ReadAddr,pBuffer,buffersize) != HAL_OK) //从EEPROM读取数据(器件地址,器件寄存器地 //址,待读取数据,待读入长度) { return EEPROM_FAIL; } return EEPROM_OK; } uint32_t EEPROM_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite) //向EEPROM写入一字节数据 //(写入的地址,待写入的数据) { uint32_t status = EEPROM_OK; if(EEPROM_IO_WriteData(EEPROMAddress,WriteAddr,&DataToWrite,1) != HAL_OK) //向EEPROM写入数据(器件地址,器件寄存器地址, //代写入数据,写入长度) { status = EEPROM_FAIL; } if(EEPROM_WaitEepromStandbyState() != EEPROM_OK) //等待EEPROM就绪 { return EEPROM_FAIL; } return status; } uint32_t EEPROM_Write(uint8_t *pBuffer,uint16_t WriteAddr,uint16_t NumToWrite) //在EEPROM里面的指定地址开始写入指定个数的数据 //(数据数组首地址,开始写入的地址,要写入数据的个数) { uint32_t status = EEPROM_OK; while(NumToWrite--) { status = EEPROM_WriteOneByte(WriteAddr,*pBuffer); //向EEPROM写入一字节数据 if(status != EEPROM_OK) { return status; } WriteAddr++; //要写入地址自加 pBuffer++; //缓冲地址自加 } return EEPROM_OK; } //实验未用到 void EEPROM_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len) //向EEPROM里面的指定地址开始写入长度为Len的数据 { u8 t; for(t=0;t<Len;t++) { EEPROM_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff); //每次写入一个(开始地址,待写入的数据) } } uint32_t EEPROM_ReadLenByte(u16 ReadAddr, u8 Len) //读取指定长度为Len的数据。 { u8 t; u32 temp=0; for(t=0;t<Len;t++) { temp <<= 8; temp += EEPROM_ReadOneByte(ReadAddr+Len-t-1); //读取指定个数数据(地址) } return temp; } |