《STM32從零開始學習歷程》@EnzoReventon
I2C向EEPROM寫入一位元組資料(I2C硬體)
相關鏈接:
I2C物理層介紹
I2C協議層介紹
I2C韌體庫介紹
STM32的I2C特性及架構介紹
STM32的EEPROM簡介
參考資料:
[野火EmbedFire]《STM32庫開發實戰指南——基于野火霸天虎開發板》
[正點原子]STM32F4開發指南-庫函式版本_V1.2
[ST]《STM32F4xx中文參考手冊》
[ATMEL]《AT24C02說明書》
開發板硬體原理圖;EEPROM原理圖,
1 實作功能
本實驗所要完成的任務是通過I2C向EEPROM中發送一個位元組的資料,
并且能夠通過串口除錯助手查看到發送狀態與現實錯誤代碼,
2 硬體設計
本實驗采用的開發板為“正點原子”探索者F4開發板,核心芯片為F407ZGT6,
使用到的外設及硬體為:USART1,I2C1,EEPROM,
USART1:PA9(T)—》RXE;PA10(R) —》TXE,將引腳使用跳線帽相連接即可,
3 軟體設計流程
- GPIO功能復用
- 初始化GPIO
- I2C初始化
- I2C使能
- 定義寫入資料函式
- 根據EEPROM寫資料流程呼叫函式,
EEPROM寫資料流程請參考:《STM32從零開始學習歷程》——STM32的EEPROM簡介,此處不做過多的詳解, - 撰寫主函式,并發送資料,
- 優化代碼(超時,故障代碼)
4 代碼分析
- I2C初始化函式
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定義GPIO結構體
I2C_InitTypeDef I2C_InitStructure; //定義I2C結構體
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //使能I2C1時鐘
GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_I2C1); //復用PB8至I2C
GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_I2C1); //復用PB9至I2C
//GPIOB8初始化SCL
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //普通輸出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //開漏輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化
//GPIOB9初始化SDA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //普通輸出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //開漏輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化
//初始化I2C結構體
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能ask回應
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ; //設定7為地址
I2C_InitStructure.I2C_ClockSpeed = 400000; //設定時鐘速度
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ; //指定時鐘占空比1/2與9/16選擇
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //設定為I2C模式
I2C_InitStructure.I2C_OwnAddress1 = 0x78; //指定自身的I2C設備地址,另一種說法:作為主機時可以不用寫?
I2C_Init(I2C1,&I2C_InitStructure); //初始化結構體
I2C_Cmd(I2C1,ENABLE); //I2C使能
}
- I2C寫入資料函式
//========================================================================================
//定義一個超時常數
#define TIME_OUT 0x000FFFFF
uint32_t count_wait = TIME_OUT;
//========================================================================================
//addr:是要寫入存盤單元的地址
//date:是要寫入的資料
uint8_t EEPROM_Byte_Write(uint8_t addr, uint8_t date)
{
//========================================================================================
//產生起始條件
I2C_GenerateSTART(I2C1,ENABLE); //產生開始信號
//重置count_wait
count_wait = TIME_OUT;
//等待EV5事件,直到成功
//checkevent函式為專門用來檢查EVx標志位的函式
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
{
count_wait--; //回圈次數自減
if(count_wait == 0) //當回圈次數減為0,標志程式運行著超時
{
return Error_Back(1);//表示程式執行卡死在EV5
}
}
//========================================================================================
//要發送的EEPROM設別地址 高四位:1010固定 低四位:A2 A1 A0 R/W 0 0 0 0 1010 0000 = 0xa0
I2C_Send7bitAddress(I2C1,0xa0,I2C_Direction_Transmitter);
//重置count_wait
count_wait = TIME_OUT;
//等待EV6事件,直到成功
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS )
{
count_wait--;
if(count_wait == 0)
{
return Error_Back(2);
}
}
//========================================================================================
//發送要寫入的存盤單元的地址
I2C_SendData(I2C1,addr);
//重置count_wait
count_wait = TIME_OUT;
//等待EV8_2事件,直到成功
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
{
count_wait--;
if(count_wait == 0)
{
return Error_Back(3);
}
}
//========================================================================================
//發送要寫入的資料
I2C_SendData(I2C1,date);
//重置count_wait
count_wait = TIME_OUT;
//等待EV8_2事件,直到成功
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
{
count_wait--;
if(count_wait == 0)
{
return Error_Back(4);
}
}
//========================================================================================
//產生結束信號
I2C_GenerateSTOP(I2C1,ENABLE);
//表示程式執行正常
return 0;
}
- 主函式
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設定系統中斷優先級分組2
delay_init(168); //初始化延時函式
uart_init(115200); //初始化串口波特率為115200
LED_Init(); //初始化LED
LCD_Init(); //LCD初始化
KEY_Init(); //按鍵初始化
IIC_Init(); //I2C初始化
EEPROM_Byte_Write(0x05, 0x22); //向EEPROM的0x05的地址中寫入0x22
printf("okok"); //若上一行代碼沒有卡死將輸出okok
}
5 效果展示
- 程式正常時:串口助手上顯示okok,表明資料已經傳輸,

- 我們將等待EV6事件完成時將條件更改,模擬EV6卡死在回圈中,下載程式,串口助手顯示錯誤3,我們便可以在程式中很快定位到EV6,錯誤3的地方進行檢查,其余的以此類推,

6 小結
這個實驗不難,主要是熟練以下暫存器的使用已經一個設計的流程,其次,代碼功能的優化也是非常重要的,在文中,錯誤代碼的顯示以及程式超時這兩個功能(從野火那兒學習來的)都是屬于代碼優化,所以我們要放寬視野,多學學人家良好的編程習慣,謝謝!請各位大佬不吝賜教!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/274477.html
標籤:其他
上一篇:Linux JSON組態檔操作
