標籤:嵌入式 單片機 電子
IIC(Inter- Integrated Circuit)匯流排是一種由 PHILIP小型股份有限公司開發的兩線式串列匯流排,用於串連微控制器及其外圍裝置。它是由資料線
SDA 和時鐘
SCL構成的串列匯流排,可
發送和接收資料。在 CPU 與被控 IC 之間、 IC 與 IC 之間進行雙向傳送, 高速 IIC 匯流排一般可達 400kbps 以上。
ALIENTEK MiniSTM32 開發板板載的 EEPROM 晶片型號為 24C02。該晶片的總容量是 256個位元組,該晶片通過 IIC 匯流排與外部串連,我們本章就通過 STM32 來實現 24C02 的讀寫。目前大部分 MCU 都帶有 IIC 匯流排介面, STM32 也不例外。但是這裡我們不使用 STM32的硬體 IIC 來讀寫 24C02,而是通過軟體類比。STM32 的硬體 IIC 非常複雜,更重要的是不穩定,故不推薦使用。所以我們這裡就通過類比來實現了。有興趣的讀者可以研究一下 STM32的硬體 IIC。
科普:EEPROM,或寫作E2PROM,全稱電子抹除式可複寫唯讀記憶體 (英語:Electrically-Erasable Programmable Read-Only Memory),是一種可以通過電子方式多次複寫的半導體存放裝置,可以在電腦上或專用裝置上擦除已有資訊,重新編程。相比EPROM,EEPROM不需要用紫外線照射,也不需取下,就可以用特定的電壓,來抹除晶片上的資訊,以便寫入新的資料。
I2C 匯流排在傳送資料過程中共有三種類型訊號, 它們分別是:開始訊號、結束訊號和應答訊號。
開始訊號: SCL 為高電平時, SDA 由高電平向低電平跳變,開始傳送資料。
結束訊號: SCL 為高電平時, SDA 由低電平向高電平跳變,結束傳送資料。
應答訊號:接收資料的 IC 在接收到 8bit 資料後,向發送資料的 IC 發出特定的低電平脈衝,表示已收到資料。 CPU 向受控單元發出一個訊號後,等待受控單元發出一個應答訊號, CPU 接收到應答訊號後,根據實際情況作出是否繼續傳遞訊號的判斷。若未收到應答訊號,由判斷為受控單元出現故障。
這些訊號中,
起始訊號是必需的,結束訊號和應答訊號,都可以不要。
24C02 的 SCL 和 SDA 分別連在 STM32 的 PC12 和 PC11 上的,串連關係24.2.1 所示:
在工程中添加兩個源檔案分別是 myiic.c 和 24cxx.c,myiic.c 檔案存放 iic 驅動代碼, 24cxx.c 檔案存放 24C02 驅動代碼
myiic.c
#include "24cxx.h"//初始化IIC介面void AT24CXX_Init(void){IIC_Init();}//在AT24CXX指定地址讀出一個資料//ReadAddr:開始讀數的地址 //傳回值 :讀到的資料u8 AT24CXX_ReadOneByte(u16 ReadAddr){ u8 temp=0; IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //發送寫命令IIC_Wait_Ack();IIC_Send_Byte(ReadAddr>>8);//發送高地址IIC_Wait_Ack(); }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //發送器件地址0XA0,寫資料 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr%256); //發送低地址IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(0XA1); //進入接收模式 IIC_Wait_Ack(); temp=IIC_Read_Byte(0); IIC_Stop();//產生一個停止條件 return temp;}//在AT24CXX指定地址寫入一個資料//WriteAddr :寫入資料的目的地址 //DataToWrite:要寫入的資料void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite){ IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //發送寫命令IIC_Wait_Ack();IIC_Send_Byte(WriteAddr>>8);//發送高地址 }else{IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //發送器件地址0XA0,寫資料 } IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr%256); //發送低地址IIC_Wait_Ack(); IIC_Send_Byte(DataToWrite); //發送位元組 IIC_Wait_Ack(); IIC_Stop();//產生一個停止條件 delay_ms(10); }//在AT24CXX裡面的指定地址開始寫入長度為Len的資料//該函數用於寫入16bit或者32bit的資料.//WriteAddr :開始寫入的地址 //DataToWrite:資料數組首地址//Len :要寫入資料的長度2,4void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len){ u8 t;for(t=0;t<Len;t++){AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);} }//在AT24CXX裡面的指定地址開始讀出長度為Len的資料//該函數用於讀出16bit或者32bit的資料.//ReadAddr :開始讀出的地址 //傳回值 :資料//Len :要讀出資料的長度2,4u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len){ u8 t;u32 temp=0;for(t=0;t<Len;t++){temp<<=8;temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1); }return temp; }//檢查AT24CXX是否正常//這裡用了24XX的最後一個地址(255)來儲存標誌字.//如果用其他24C系列,這個地址要修改//返回1:檢測失敗//返回0:檢測成功u8 AT24CXX_Check(void){u8 temp;temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX if(temp==0X55)return 0; else//排除第一次初始化的情況{AT24CXX_WriteOneByte(255,0X55); temp=AT24CXX_ReadOneByte(255); if(temp==0X55)return 0;}return 1; }//在AT24CXX裡面的指定地址開始讀出指定個數的資料//ReadAddr :開始讀出的地址 對24c02為0~255//pBuffer :資料數組首地址//NumToRead:要讀出資料的個數void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead){while(NumToRead){*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);NumToRead--;}} //在AT24CXX裡面的指定地址開始寫入指定個數的資料//WriteAddr :開始寫入的地址 對24c02為0~255//pBuffer :資料數組首地址//NumToWrite:要寫入資料的個數void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite){while(NumToWrite--){AT24CXX_WriteOneByte(WriteAddr,*pBuffer);WriteAddr++;pBuffer++;}}
myiic.h
#ifndef __MYIIC_H#define __MYIIC_H#include "sys.h"#include "delay.h"//IO方向設定#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}//IO操作函數 #define IIC_SCL PCout(12)//SCL#define IIC_SDA PCout(11)//SDA #define READ_SDA PCin(11)//輸入SDA //IIC所有操作函數void IIC_Init(void);//初始化IIC的IO口 void IIC_Start(void);//發送IIC開始訊號void IIC_Stop(void);//發送IIC停止訊號void IIC_Send_Byte(u8 txd);//IIC發送一個位元組u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個位元組u8 IIC_Wait_Ack(void);//IIC等待ACK訊號void IIC_Ack(void);//IIC發送ACK訊號void IIC_NAck(void);//IIC不發送ACK訊號void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);u8 IIC_Read_One_Byte(u8 daddr,u8 addr); #endif
24cxx.c
#include "24cxx.h"//初始化IIC介面void AT24CXX_Init(void){IIC_Init();}//在AT24CXX指定地址讀出一個資料//ReadAddr:開始讀數的地址 //傳回值 :讀到的資料u8 AT24CXX_ReadOneByte(u16 ReadAddr){ u8 temp=0; IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //發送寫命令IIC_Wait_Ack();IIC_Send_Byte(ReadAddr>>8);//發送高地址IIC_Wait_Ack(); }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //發送器件地址0XA0,寫資料 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr%256); //發送低地址IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(0XA1); //進入接收模式 IIC_Wait_Ack(); temp=IIC_Read_Byte(0); IIC_Stop();//產生一個停止條件 return temp;}//在AT24CXX指定地址寫入一個資料//WriteAddr :寫入資料的目的地址 //DataToWrite:要寫入的資料void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite){ IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //發送寫命令IIC_Wait_Ack();IIC_Send_Byte(WriteAddr>>8);//發送高地址 }else{IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //發送器件地址0XA0,寫資料 } IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr%256); //發送低地址IIC_Wait_Ack(); IIC_Send_Byte(DataToWrite); //發送位元組 IIC_Wait_Ack(); IIC_Stop();//產生一個停止條件 delay_ms(10); }//在AT24CXX裡面的指定地址開始寫入長度為Len的資料//該函數用於寫入16bit或者32bit的資料.//WriteAddr :開始寫入的地址 //DataToWrite:資料數組首地址//Len :要寫入資料的長度2,4void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len){ u8 t;for(t=0;t<Len;t++){AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);} }//在AT24CXX裡面的指定地址開始讀出長度為Len的資料//該函數用於讀出16bit或者32bit的資料.//ReadAddr :開始讀出的地址 //傳回值 :資料//Len :要讀出資料的長度2,4u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len){ u8 t;u32 temp=0;for(t=0;t<Len;t++){temp<<=8;temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1); }return temp; }//檢查AT24CXX是否正常//這裡用了24XX的最後一個地址(255)來儲存標誌字.//如果用其他24C系列,這個地址要修改//返回1:檢測失敗//返回0:檢測成功u8 AT24CXX_Check(void){u8 temp;temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX if(temp==0X55)return 0; else//排除第一次初始化的情況{AT24CXX_WriteOneByte(255,0X55); temp=AT24CXX_ReadOneByte(255); if(temp==0X55)return 0;}return 1; }//在AT24CXX裡面的指定地址開始讀出指定個數的資料//ReadAddr :開始讀出的地址 對24c02為0~255//pBuffer :資料數組首地址//NumToRead:要讀出資料的個數void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead){while(NumToRead){*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);NumToRead--;}} //在AT24CXX裡面的指定地址開始寫入指定個數的資料//WriteAddr :開始寫入的地址 對24c02為0~255//pBuffer :資料數組首地址//NumToWrite:要寫入資料的個數void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite){while(NumToWrite--){AT24CXX_WriteOneByte(WriteAddr,*pBuffer);WriteAddr++;pBuffer++;}}
24cxx.h
#ifndef __24CXX_H#define __24CXX_H#include "myiic.h" #include "delay.h"#define AT24C01127#define AT24C02255#define AT24C04511#define AT24C081023#define AT24C162047#define AT24C324095#define AT24C64 8191#define AT24C12816383#define AT24C25632767 #define EE_TYPE AT24C02u8 AT24CXX_ReadOneByte(u16 ReadAddr);//指定地址讀取一個位元組void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);//指定地址寫入一個位元組void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址開始寫入指定長度的資料u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len);//指定地址開始讀取指定長度資料void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);//從指定地址開始寫入指定長度的資料void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);//從指定地址開始讀出指定長度的資料u8 AT24CXX_Check(void);//檢查器件void AT24CXX_Init(void);//初始化IIC#endif
代碼有點長。。但其實配好了用到一般只有讀和寫兩個函數如下:
//在AT24CXX裡面的指定地址開始讀出指定個數的資料//ReadAddr :開始讀出的地址 對24c02為0~255//pBuffer :資料數組首地址//NumToRead:要讀出資料的個數void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead){while(NumToRead){*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);NumToRead--;}} //在AT24CXX裡面的指定地址開始寫入指定個數的資料//WriteAddr :開始寫入的地址 對24c02為0~255//pBuffer :資料數組首地址//NumToWrite:要寫入資料的個數void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite){while(NumToWrite--){AT24CXX_WriteOneByte(WriteAddr,*pBuffer);WriteAddr++;pBuffer++;}}
參數解釋的很明白也很好理解。。
主函數
#include "sys.h"#include "delay.h"#include "usart.h"#include "led.h"#include "lcd.h"#include "key.h"#include "myiic.h"#include "24cxx.h"const u8 TEXT_Buffer[]={"MY IIC TEST"};#define SIZE sizeof(TEXT_Buffer)void init(void){NVIC_Configuration();delay_init();uart_init(9600);LED_Init();KEY_Init();LCD_Init();AT24CXX_Init();POINT_COLOR=RED;//設定字型為紅色 LCD_ShowString(60,40,200,24,24,"IIC TEST");LCD_ShowString(60,70,200,16,16,"~~~~~~~~~");LCD_ShowString(60,90,200,16,16,"BY---yh");LCD_ShowString(60,110,200,16,16,"2015/1/26");LCD_ShowString(60,130,200,16,16,"WK_UP:Write KEY0:Read");while(AT24CXX_Check())//檢測不到24c02{LCD_ShowString(60,150,200,16,16,"24C02 Check Failed!");delay_ms(500);LCD_ShowString(60,150,200,16,16,"Please Check! ");delay_ms(500);LED0=!LED0;//DS0閃爍}LCD_ShowString(60,150,200,16,16,"24C02 Ready!");POINT_COLOR=BLUE;}int main(void){u8 key;u16 i=0;u8 datatemp[SIZE];init();while(1){key=KEY_Scan(0);if(key==WK_UP_PRES)//寫{LCD_Fill(0,170,239,319,WHITE);//清除半屏 LCD_ShowString(60,170,200,16,16,"Start Write 24C02....");AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);LCD_ShowString(60,170,200,16,16,"24C02 Write Finished!");//提示傳送完成}if(key==KEY0_PRES)//KEY0 按下,讀取字串並顯示{ LCD_ShowString(60,170,200,16,16,"Start Read 24C02.... ");AT24CXX_Read(0,datatemp,SIZE);LCD_ShowString(60,170,200,16,16,"The Data Readed Is: ");//提示傳送完成LCD_ShowString(60,190,200,16,16,datatemp);//顯示讀到的字串}i++;delay_ms(10);if(i==20){LED0=!LED0;i=0;}}}
cortex_m3_stm32嵌入式學習筆記(二十):IIC實驗(I2C串列匯流排)