21.1實驗內容
通過本實驗主要學習以下內容:
21.2實驗原理
21.2.1AT24C16 EEPROM的工作原理
下圖為AT24CXX系列EEPROM相關參數,由該圖可知,AT24C16的存儲容量為16Kbit,共2048字節,共128頁,每頁為16字節。
由下圖可知,AT24C16由8塊組成,每塊256字節。
I2C開始信號后,第一個字節為器件地址,由1010+3位塊地址+1位讀寫標志組成,3位塊地址剛好可以表示8個塊, 所以一次寫完256字節,換到下一下塊的時候,要重新更改器件地址。
AT24C16支持頁寫入模式,一次最多可支持寫入16字節。主機每發送一個字節,24c16收到確認,內部地址遞增(僅限低4bit,所以1次可寫16字節)。
21.2.2IIC接口原理
GD32F30X系列MCU的I2C接口模塊實現了I2C協議的標速模式,快速模式以及快速+模式,具備CRC計算和校驗功能、支持SMBus(系統管理總線)和PMBus(電源管理總線),此外還支持多主機I2C總線架構,其主要特性如下:
?并行總線至I2C總線協議的轉換及接口;
?同一接口既可實現主機功能又可實現從機功能;
?主從機之間的雙向數據傳輸;
?支持7位和10位的地址模式和廣播尋址;
?支持I2C多主機模式;
?支持標速(最高100 KHz),快速(最高400 KHz)和快速+模式(最高1MHz);
?從機模式下可配置的SCL主動拉低;
?支持DMA模式;
?兼容SMBus 2.0和PMBus;
?兩個中斷:字節成功發送中斷和錯誤事件中斷;
?可選擇的PEC(報文錯誤校驗)生成和校驗。
IIC模塊結構框圖如下所示。
21.3硬件設計
EEPROM硬件電路圖如下所示,IIC引腳使用PB10和PB11引腳,SDA和SCL總線通過4.7K電阻上拉,且對地接30pf電容以及100歐姆串阻濾波。
21.4代碼解析
21.4.1EEPROM初始化配置函數
EEPROM初始化配置函數如下,主要實現對IIC總線引腳配置以及IIC模塊配置。
C void bsp_eeprom_init_AT24C16(void) { driver_i2c_init(&EEPROM_I2C); } void driver_i2c_init(typdef_i2c_struct *i2cx) { rcu_periph_clock_enable(i2cx->rcu_i2c_x); i2c_deinit(i2cx->i2c_x); driver_gpio_general_init(i2cx->i2c_scl_gpio); driver_gpio_general_init(i2cx->i2c_sda_gpio); /* I2C clock configure */ i2c_clock_config(i2cx->i2c_x, i2cx->frequency, I2C_DTCY_2); /* I2C address configure */ i2c_mode_addr_config(i2cx->i2c_x, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, i2cx->slave_addr); /* enable I2C0 */ i2c_enable(i2cx->i2c_x); /* enable acknowledge */ i2c_ack_config(i2cx->i2c_x, I2C_ACK_ENABLE); } |
21.4.2EEPROM buf寫入接口函數
EEPROM buf寫入接口函數實現如下,通過該函數可實現對AT24C16任意地址的多字節寫入。內部已根據地址和寫入長度自動識別從機地址以及對應的塊,然后寫入正確的地址空間。
C EEPROM_STATE eeprom_buffer_write_AT24C16(uint8_t* p_buffer, uint16_t write_address, uint16_t number_of_byte) { uint8_t number_of_page = 0, number_of_single = 0, address = 0, count = 0; uint8_t deviceId; address = write_address % I2C_PAGE_SIZE; count = I2C_PAGE_SIZE - address; number_of_page = number_of_byte / I2C_PAGE_SIZE; number_of_single = number_of_byte % I2C_PAGE_SIZE; if(write_address+write_address>EEPROM_SIZE) { return EEPROM_ERROR; } /* if write_address is I2C_PAGE_SIZE aligned */ if(0 == address){ while(number_of_page--){ deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer,I2C_PAGE_SIZE) == DRV_ERROR) { return EEPROM_ERROR; } if(eeprom_wait_standby_state(&EEPROM_I2C) == EEPROM_ERROR) { return EEPROM_ERROR; } write_address += I2C_PAGE_SIZE; p_buffer += I2C_PAGE_SIZE; } if(0 != number_of_single){ deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_single)==DRV_ERROR) { return EEPROM_ERROR; } if(eeprom_wait_standby_state(&EEPROM_I2C) == EEPROM_ERROR) { return EEPROM_ERROR; } } return EEPROM_SUCCESS; }else{ /* if write_address is not I2C_PAGE_SIZE aligned */ if(number_of_byte < count){? deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_byte)==DRV_ERROR) { return EEPROM_ERROR; } if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR) { return EEPROM_ERROR; } }else{ number_of_byte -= count; number_of_page = number_of_byte / I2C_PAGE_SIZE; number_of_single = number_of_byte % I2C_PAGE_SIZE; if(0 != count){ deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, count)==DRV_ERROR) { return EEPROM_ERROR; } if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR) { return EEPROM_ERROR; } write_address += count; p_buffer += count; } /* write page */ while(number_of_page--){ deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, I2C_PAGE_SIZE)==DRV_ERROR) { return EEPROM_ERROR; } if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR) { return EEPROM_ERROR; } write_address += I2C_PAGE_SIZE; p_buffer += I2C_PAGE_SIZE; } /* write single */ if(0 != number_of_single){ deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_single)==DRV_ERROR) { return EEPROM_ERROR; } if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR) { return EEPROM_ERROR; } } } return EEPROM_SUCCESS; } } |
21.4.3EEPROM buf讀取接口函數
EEPROM buf讀取接口函數實現如下,通過該函數可實現對EEPROM任意地址的多字節數據讀取,內部也對讀取的地址進行自動識別從機地址。
C EEPROM_STATE eeprom_buffer_read_AT24C16(uint8_t* p_buffer, uint16_t read_address, uint16_t number_of_byte) { uint8_t rNum=0; //讀取的數據長度 uint16_t lenLeft=number_of_byte;//剩余的數據長度 uint8_t deviceId;//讀取的器件地址 if(read_address+number_of_byte>EEPROM_SIZE)//如果讀取的長度加上讀取地址超過了EEPROM的空間大小,則報錯誤 { return EEPROM_ERROR; } /*calculate the current read position to know how many word can read continully*/ rNum=16-read_address & 0x0F; if(rNum == 0) rNum=16; rNum = lenLeft>=rNum ? rNum : lenLeft;//剩余未讀字節數如果大于rNum, 則讀rNum個,如果小于rNum,則一次讀完了 /*read the data from e2prom*/ while(lenLeft) { //這里計算頁地址,當地址小于256時,右移8位會小于0,所以器件地址為基地址A1 //如果讀取的地址大于256時,右移8位則不會小于0,所以器件地址為 基地址A1 | 3位頁地址 deviceId=(read_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((read_address>>7)&0x0E)):EEPROM_ADDR ; if(driver_i2c_mem_poll_read(&EEPROM_I2C,deviceId,read_address,MEM_ADDRESS_8BIT,p_buffer,rNum)==DRV_ERROR) { // printf("i2c read error\r\n"); return EEPROM_ERROR; } read_address+=rNum;//已經讀了rNum個了,所以地址后移rNum個 lenLeft-=rNum;//剩余未讀數據減少rNum個 p_buffer+=rNum; rNum=lenLeft>16? 16 : lenLeft;//如果剩余大于16個,則下次再讀16個,如果小于,則一次讀完 } return EEPROM_SUCCESS; } |
21.4.4EEPROM讀寫實驗主函數
EEPROM讀寫實驗主函數如下所示。通過該實驗實現對AT24C16任意地址256字節的寫入、讀取以及校驗測試。
C int main(void) { uint16_t i; uint8_t i2c_buffer_write[BUFFER_SIZE]; uint8_t i2c_buffer_read[BUFFER_SIZE]; bsp_eeprom_init_AT24C16(); /* initialize i2c_buffer_write */ for(i = 0;i < BUFFER_SIZE;i++){? i2c_buffer_write[i]=i; // printf("0x%02X ",i2c_buffer_write[i]); // if(15 == i%16){ // printf("\r\n"); // } } if(eeprom_buffer_write_AT24C16(i2c_buffer_write,0x0153,BUFFER_SIZE)==EEPROM_SUCCESS) { __nop(); } if(eeprom_buffer_read_AT24C16(i2c_buffer_read,0x0153,BUFFER_SIZE)==EEPROM_SUCCESS) { __nop(); } /* compare the read buffer and write buffer */ for(i = 0;i < BUFFER_SIZE;i++){ if(i2c_buffer_read[i] != i2c_buffer_write[i]){ __nop(); // printf("0x%02X ", i2c_buffer_read[i]); // printf("Err:data read and write aren't matching.\n\r"); // return I2C_FAIL; } //printf("0x%02X ", i2c_buffer_read[i]); // if(15 == i%16){ // printf("\r\n"); // } } __nop(); // printf("I2C-AT24C02 test passed!\n\r"); while (1) { } } |
21.5實驗結果
將本實驗歷程燒錄到紅楓派開發板中,運行后,可通過串口打印測試結果,可實現對于AT24C16任意地址寫入、讀取以及校驗。
-
單片機
+關注
關注
6037文章
44558瀏覽量
635235 -
嵌入式
+關注
關注
5082文章
19123瀏覽量
305151 -
EEPROM
+關注
關注
9文章
1020瀏覽量
81597 -
I2C
+關注
關注
28文章
1487瀏覽量
123740 -
開發板
+關注
關注
25文章
5050瀏覽量
97456
發布評論請先 登錄
相關推薦
評論