bsp_i2c.h
#ifndef __BSP_I2C_H
#define __BSP_I2C_H
#include "gd32e10x.h"
//SCL
#define EEPROM_I2C_SCL_GPIO_CLK RCU_GPIOB
#define EEPROM_I2C_SCL_PORT GPIOB
#define EEPROM_I2C_SCL_PIN GPIO_PIN_10
//SDA
#define EEPROM_I2C_SDA_GPIO_CLK RCU_GPIOB
#define EEPROM_I2C_SDA_PORT GPIOB
#define EEPROM_I2C_SDA_PIN GPIO_PIN_11
//控制闆子上另一個紅燈亮滅
#define LED_RED_ANOTHER_ON PCout(13)=0
#define LED_RED_ANOTHER_OFF PCout(13)=1
//I2C
#define EEPROM_I2Cx I2C1
#define EEPROM_I2C_CLK RCU_I2C1
#define EEPROM_I2Cx_SPPED 100000
/* 下面兩個位址都是取高7位作為7位的位址*/
/* 這個位址隻要與STM32外挂的I2C器件位址不一樣即可 */
#define I2Cx_OWN_ADDRESS7 0X0A
#define EEPROM_ADDRESS 0xA0
#define EEPROM_I2Cx_FLAG_TIMEOUT ((uint32_t)0xFFFFFFFF)
void SCL_GPIO_Config(void);
void SDA_GPIO_Config(void);
void LED_RED_ANOTHER_GPIO_Config(void);
void TP1_GPIO_Config(void);
void EEPROM_I2C_Config(void);
uint8_t I2C_EE_ByteWrite(uint8_t* sBuffer, uint8_t WriteAddr);
void Wait_EEPROMStandbyState(void);
uint8_t Wait_I2CBus_IdleState(uint32_t wait_delay);
uint8_t I2C_EE_Random_Read(uint8_t* pBuffer, uint8_t ReadAddr, uint8_t NumByteToRead);
#endif
bsp_i2c.c
#include "bsp_i2c.h"
#include "bsp_gpio.h"
void SCL_GPIO_Config(void)
{
rcu_periph_clock_enable(EEPROM_I2C_SCL_GPIO_CLK);
gpio_init(EEPROM_I2C_SCL_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, EEPROM_I2C_SCL_PIN);
}
void SDA_GPIO_Config(void)
{
rcu_periph_clock_enable(EEPROM_I2C_SDA_GPIO_CLK);
gpio_init(EEPROM_I2C_SDA_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, EEPROM_I2C_SDA_PIN);
}
void LED_RED_ANOTHER_GPIO_Config(void)
{
rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOC, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
}
void TP1_GPIO_Config(void)
{
rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
}
/*!
\brief I2C1工作參數配置
\param[in] none
\param[out] none
\retval none
*/
void EEPROM_I2C_Config(void)
{
rcu_periph_clock_enable(EEPROM_I2C_CLK);
// rcu_periph_clock_enable(RCU_AF);
/* 模式選I2C 模式,位址選擇7位位址,最後設定從機位址 */
i2c_mode_addr_config(EEPROM_I2Cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, EEPROM_ADDRESS);
/* I2C時鐘速率設為400KHz,占空比選擇 T_low/T_high=2 */
i2c_clock_config(EEPROM_I2Cx, EEPROM_I2Cx_SPPED, I2C_DTCY_2);
i2c_ack_config(EEPROM_I2Cx, I2C_ACK_ENABLE);
// i2c_ackpos_config(EEPROM_I2Cx,I2C_ACKPOS_CURRENT);
/* 使能EEPROM_I2Cx*/
i2c_enable(EEPROM_I2Cx);
}
/*!
\brief 用I2C外設對EEPROM某個位址寫入一個位元組的資料,詳細過程嚴格按照《使用者手冊》
第416頁介紹的主機發送模式下的軟體流程
\param[in] sBuffer:緩沖區指針
\param[in] WriteAddr:被寫入的位址
\param[out] none
\retval 傳回的數字表示錯誤發生的地方
*/
uint8_t I2C_EE_ByteWrite(uint8_t* sBuffer, uint8_t WriteAddr)
{
uint32_t I2C_Timeout;
//發送起始位
i2c_start_on_bus(EEPROM_I2Cx);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_SBSEND)==0)
{
if((I2C_Timeout--) == 0)
return 1;
}
// //《使用者手冊》第416頁第3條說了隻要往I2C_DATA寄存器寫入位址就能硬體清除ADDSEND位
// i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_SBSEND );
//發送EEPROM位址
i2c_master_addressing(EEPROM_I2Cx,EEPROM_ADDRESS,I2C_TRANSMITTER);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
//《使用者手冊》第416頁第4條說了通過讀I2C_STAT0寄存器然後讀I2C_STAT1寄存器
// 就能硬體清除ADDSEND位,但是我親自實驗後發現,就算讀取了這兩個寄存器的值也不能
// 清除ADDSEND位,是以我放棄讀取I2C_STAT0、I2C_STAT1這兩個寄存器,隻讀取I2C_STAT0
// 這一個寄存器
// while((i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_ADDSEND)==0)&&(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_TRS)==0))
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_ADDSEND)==0)
{
if((I2C_Timeout--) == 0)
return 2;
}
//用軟體清除ADDSEND位
i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_ADDSEND );
//發送EEPROM内部的位址
i2c_data_transmit(EEPROM_I2Cx,WriteAddr);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_TBE)==0)
{
if((I2C_Timeout--) == 0)
return 3;
}
// //《使用者手冊》第416頁第6條說了當寫第二個位元組到I2C_DATA,此時TBE會被硬體清零
// i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_TBE );
//發送一個位元組的資料,發送結束後不需要對标志位清0,因為在發出停止位硬體會對這位進行清0
i2c_data_transmit(EEPROM_I2Cx,*sBuffer);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_TBE)==0)
{
if((I2C_Timeout--) == 0)
return 4;
}
// //《使用者手冊》第416頁第6條說了當寫第二個位元組到I2C_DATA,此時TBE會被硬體清零
// i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_TBE );
i2c_stop_on_bus(EEPROM_I2Cx);
return 0;
}
/*!
\brief 等待EEPROM内部資料擦寫完畢,如果擦寫完畢,就能産生ACK
\param[in] none
\param[out] none
\retval none
*/
void Wait_EEPROMStandbyState(void)
{
//ADDSEND是I2C_STAT0寄存器的值,但是變量名中不能帶數字,
//是以沒有用I2C0_STAT_ADDSEND作為變量名
uint8_t I2C_STAT_ADDSEND = 0;
do
{
/* Send START condition */
i2c_start_on_bus(EEPROM_I2Cx);
/* 讀I2C1 SR1寄存器的ADDR位 */
I2C_STAT_ADDSEND = i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_ADDSEND);
/* Send EEPROM address for write */
i2c_master_addressing(EEPROM_I2Cx,EEPROM_ADDRESS,I2C_TRANSMITTER);
}while(!I2C_STAT_ADDSEND);
/* Clear AERR flag */
i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_AERR );
/* STOP condition */
i2c_stop_on_bus(EEPROM_I2Cx);
}
/*!
\brief 等待到總線沒有I2C通訊
\param[in] wait_delay:超過這個延時時間就報警
\param[out] none
\retval 錯誤碼
*/
uint8_t Wait_I2CBus_IdleState(uint32_t wait_delay)
{
uint32_t Wait_Time=wait_delay;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_I2CBSY))
{
if((Wait_Time--) == 0) return 1;
}
return 0;
}
/*!
\brief 利用《使用者手冊》中第417頁介紹的A方案來接收資料
\param[in] NumByteToRead:被讀取的資料個數
\param[in] WriteAddr:EEPROM的讀取位址
\param[out] pBuffer:緩沖區指針
\retval 傳回的數字表示錯誤發生的地方
*/
uint8_t I2C_EE_Random_Read(uint8_t* pBuffer, uint8_t ReadAddr, uint8_t NumByteToRead)
{
uint32_t I2C_Timeout;
//發送起始位
i2c_start_on_bus(EEPROM_I2Cx);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_SBSEND)==0)
{
if((I2C_Timeout--) == 0)
return 1;
}
// //《使用者手冊》第417頁第3條說了隻要往I2C_DATA寄存器寫入位址就能硬體清除SBSEND位
// i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_SBSEND );
//發送EEPROM位址,第一次方向是發送資料
i2c_master_addressing(EEPROM_I2Cx,EEPROM_ADDRESS,I2C_TRANSMITTER);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_ADDSEND)==0)
{
if((I2C_Timeout--) == 0)
return 2;
}
//《使用者手冊》第418頁第4條說了可以硬體清除ADDSEND位
i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_ADDSEND );
//發送EEPROM内部的位址
i2c_data_transmit(EEPROM_I2Cx,ReadAddr);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_TBE)==0)
{
if((I2C_Timeout--) == 0)
return 3;
}
//《使用者手冊》第416頁第6條說了當寫第二個位元組到I2C_DATA,此時TBE會被硬體清零
// 但是下個操作沒有寫第二個位元組到I2C_DATA的程式,是以用軟體對這個标志位清0
i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_TBE );
//發送起始位
i2c_start_on_bus(EEPROM_I2Cx);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_SBSEND)==0)
{
if((I2C_Timeout--) == 0)
return 4;
}
// //《使用者手冊》第417頁第3條說了隻要往I2C_DATA寄存器寫入位址就能硬體清除ADDSEND位
// i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_SBSEND );
//發送EEPROM位址,第二次方向是接收資料
i2c_master_addressing(EEPROM_I2Cx,EEPROM_ADDRESS,I2C_RECEIVER);
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_ADDSEND)==0)
{
if((I2C_Timeout--) == 0)
return 5;
}
i2c_flag_clear(EEPROM_I2Cx,I2C_FLAG_ADDSEND );
/*讀取NumByteToRead個位元組的資料*/
while(NumByteToRead)
{
I2C_Timeout = EEPROM_I2Cx_FLAG_TIMEOUT;
while(i2c_flag_get(EEPROM_I2Cx,I2C_FLAG_RBNE)==0)
{
if((I2C_Timeout--) == 0)
return 6;
}
/* Read a byte from the EEPROM */
*pBuffer = i2c_data_receive(EEPROM_I2Cx);
pBuffer++;
NumByteToRead--;
}
return 0;
}
main.c
uint8_t sBuffer[5]={0x4E,0x01,0x32,0x11,0x9A};
uint8_t dBuffer[5];
uint32_t Write_Error_Type=0;
uint32_t Read_Error_Type=0;
//需要讀多少位元組的資料
uint8_t NumByteToRead=1;
uint8_t i=0;
//存儲晶片儲存資料的首位址
uint8_t Data_Write_BaseAddress=0;
//需要存多少位元組的資料
uint8_t NumByteToWrite=2;
void test_i2c(void)
{
LED_RED_GPIO_Config();
LED_RED_OFF;
LED_GREEN_GPIO_Config();
LED_GREEN_OFF;
LED_RED_ANOTHER_GPIO_Config();
LED_RED_ANOTHER_OFF;
/*如果不注釋掉這兩句,那麼執行下面代碼時會一直卡在Wait_EEPROMStandbyState()處,
這是GD32E103RBT6的BUG*/
// TP1_GPIO_Config();
// PCout(14)=0;
SCL_GPIO_Config();
SDA_GPIO_Config();
EEPROM_I2C_Config();
/****************************寫入資料***************************/
for(i=0;i<NumByteToWrite;i++)
{
// PCout(14)^=1;
//先寫入一個位元組的資料,寫入失敗的話讓紅燈亮
Write_Error_Type=I2C_EE_ByteWrite(sBuffer+i,Data_Write_BaseAddress+i);
if(Write_Error_Type!=0)
{
LED_RED_ANOTHER_ON;
while(1);
}
//等待總線通訊結束
Wait_EEPROMStandbyState();
if(Wait_I2CBus_IdleState(0xFFFFFFFF)!=0)
{
LED_RED_ANOTHER_ON;
while(1);
}
}
// /****************************讀取資料***************************************/
// //讀取一個位元組的資料,讀取錯誤讓紅燈亮
// Read_Error_Type=I2C_EE_Random_Read(dBuffer,Data_Write_BaseAddress,NumByteToRead);
// if(Read_Error_Type!=0)
// {
// LED_RED_ON;
// while(1);
// }
//
//
// // //等待總線通訊結束
// Wait_EEPROMStandbyState();
// if(Wait_I2CBus_IdleState(0xFFFFFF)!=0)
// {
// LED_RED_ANOTHER_ON;
// while(1);
// }
//
//
//
// //讀取一個位元組的資料,讀取錯誤讓紅燈亮
// Read_Error_Type=I2C_EE_Random_Read(dBuffer+1,Data_Write_BaseAddress+1,NumByteToRead);
// if(Read_Error_Type!=0)
// {
// LED_RED_ON;
// while(1);
// }
//綠燈亮代表代碼執行到這一步
LED_GREEN_ON;
while(1);
}