【专题STM32F103】PB6接SCL,PB7接SDA,IIC通信访问 24C02, STM32F103的HAL版本
在野火霸道板子上,已有at24c02,256个字节。
/*测试代码*/ static uint8_t flag; uint8_t data_w[]={1,2,3,4,5}; uint8_t data_r[5]={0}; AT24CXX_Init();//初始化IIC接口 flag = AT24CXX_Check(); //检查器件 if(flag == 0) { u16 WriteAddr =0x16; u16 NumToWrite = sizeof(data_w); AT24CXX_Write(WriteAddr,data_w,NumToWrite); //从指定地址开始写入指定长度的数据 AT24CXX_Read(WriteAddr,data_r,NumToWrite); //从指定地址开始读出指定长度的数据 }
IIC总线驱动代码
iic.h文件
#ifndef __MYIIC_H #define __MYIIC_H //#include "sys.h" #include "main.h" //IO方向设置 PA11 // 每组的高8位 //#define SDA_IN() {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=0X00008000;} //#define SDA_OUT() {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=0X00003000;} // 每组的低8位 PB7 #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X80000000;} #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X30000000;} //IO操作函数 //#define IIC_SCL PAout(12) //SCL PA12 修改为PB6 //#define IIC_SDA PAout(11) //SDA PA11 修改为PB7 //#define READ_SDA PAin(11) //输入SDA //IO操作函数 #define IIC_SCL PBout(6) //SCL PA12 修改为PB6 #define IIC_SDA PBout(7) //SDA PA11 修改为PB7 #define READ_SDA PBin(7) //输入SDA 修改为PB7 //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
iic.c文件
#include "iic.h" //#include "delay.h" //初始化IIC 板 PA11 PA12 void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); //使能GPIOA时钟 __HAL_RCC_GPIOB_CLK_ENABLE ( );//启动外设工作的时钟 GPIO_InitStructure.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP ; //推挽输出 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6|GPIO_PIN_7,GPIO_PIN_SET); // 输出高 } //产生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; if((txd&0x80)>>7) IIC_SDA=1; else IIC_SDA=0; 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; }
24cxx.h文件
#ifndef __24CXX_H #define __24CXX_H #include "iic.h" ////////////////////////////////////////////////////////////////////////////////// #define AT24C01 127 #define AT24C02 255 #define AT24C04 511 #define AT24C08 1023 #define AT24C16 2047 #define AT24C32 4095 #define AT24C64 8191 #define AT24C128 16383 #define AT24C256 32767 #define EE_TYPE AT24C02 u8 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
24cxx.c文件
#include "24cxx.h" //#include "delay.h" //初始化IIC接口 void AT24CXX_Init(void)//初始化IIC接口 { 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(); //24c256!!!!!!!!!!!!!!!!!!!! }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();//产生一个停止条件 HAL_Delay(/*delay_ms(*/10); } //在AT24CXX里面的指定地址开始写入长度为Len的数据 //该函数用于写入16bit或者32bit的数据. //WriteAddr :开始写入的地址 //DataToWrite:数据数组首地址 //Len :要写入数据的长度2,4 void 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,4 u32 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++; } }