IIC
一、用於連接CPU和外圍電路的總線,一般有兩根信號線,一根是雙向的數據線SDA,另一根是時鐘線SCL。I2C總線上允許連接多個微處理器以及各種外圍設備,爲了進行通訊,每個接到I2C總線的設備都有一個唯一的地址。
二、信號。IIC在每次傳輸完一字節後需要應答信號。
- 在進行通信時,主控要告訴外設開始和結束,分別對應開始和結束信號。
- IIC在每次傳輸完一字節後需要應答信號。
- 在驅動大部分外設時,傳輸一個字節數據需要SCL==1。此時SDA上的數據認爲是可信的。這也就是說如果對於信號沒有特殊的要求,這部分不需要改寫。這裏直接用的正點原子的,就是初始化和配置端口需要自己改。
#include "myiic.h" #include "delay.h" ////////////////////////////////////////////////////////////////////////////////// //本程序只供學習使用,未經作者許可,不得用於其它任何用途 //ALIENTEK STM32F407開發板 //IIC 驅動代碼 //正點原子@ALIENTEK //技術論壇:www.openedv.com //創建日期:2014/5/6 //版本:V1.0 //版權所有,盜版必究。 //Copyright(C) 廣州市星翼電子科技有限公司 2014-2024 //All rights reserved ////////////////////////////////////////////////////////////////////////////////// //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB時鐘 //GPIOB8,B9初始化設置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推輓輸出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 IIC_SCL=1; IIC_SDA=1; } //產生IIC起始信號 void IIC_Start(void) { SDA_OUT(); //sda線輸出 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0;//鉗住I2C總線,準備發送或接收數據 } //產生IIC停止信號 void IIC_Stop(void) { SDA_OUT();//sda線輸出 IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1;//發送I2C總線結束信號 delay_us(4); } //等待應答信號到來 //返回值:1,接收應答失敗 // 0,接收應答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA設置爲輸入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0;//時鐘輸出0 return 0; } //產生ACK應答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //不產生ACK應答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC發送一個字節 //返回從機有無應答 //1,有應答 //0,無應答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低時鐘開始數據傳輸 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); //對TEA5767這三個延時都是必須的 IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //讀1個字節,ack=1時,發送ACK,ack=0,發送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA設置爲輸入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck();//發送nACK else IIC_Ack(); //發送ACK return receive; }
三、我們有了驅動的信號,就可以做讀或者寫數據了。這是我們就要看好數據手冊了。主要是他需要的地址信息(每個外設唯一)和信號指令(讀或者寫)。
- 先來看24c02的首地址,A2/1/0都是地址位。 當24c02的三個A管腳都置零時,如果寫數據則首地址位是1010 0000,如果讀數據也就是1010 0001。
- 在看fdc2214的首地址。他是這麼說的“當ADDR pin設置爲低時,FDC I2C地址爲0x2A;當ADDR pin設置爲高時,FDC I2C地址爲0x2B”。
- 然後我們看他們給我們讀取或寫入數據的規則。拿24c02讀數據來說: 他說我們需要先寫一個起始條件,然後器件地址接應答,在進入讀模式讀取8位數據,最後停止。
//在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);//發送高地址 }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; }
- 在看fdc2214 我們需要寫入器件地址和寄存器地址,在進行數據讀取,最後無應答和停止信號,只不過這個寄存器是16位的。我們需要讀兩次。其實和上面24c02的代碼是很像的。
u16 FDC_Read(u8 reg) //核心代碼 //不需修改 { u16 res; FDC_IIC_Start(); FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|0);//發送器件地址+寫命令 FDC_IIC_Wait_Ack(); //等待應答 FDC_IIC_Send_Byte(reg); //寫寄存器地址 FDC_IIC_Wait_Ack(); //等待應答 FDC_IIC_Start(); FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|1);//發送器件地址+讀命令 FDC_IIC_Wait_Ack(); //等待應答 res=FDC_IIC_Read_Byte(1)<<8;//讀取數據,發送ACK res|=FDC_IIC_Read_Byte(0);//讀取數據,發送nACK FDC_IIC_Stop(); //產生一個停止條件 return res; }
結束了。