先说明一下遇到的问题:
1、在进行单字节数据循环读写的时候,循环写的数据少了(如<=100)可以,但是多了就会挂(目前随即选了发送120个就挂了)。
2、在多字节读取的函数中,读写的数据量在20(随即选的)以内时木有问题,但是如果一次写的字节数多了(>100),在多字节读的函数中执行到读最后一个字节数据时,就会挂在while();等待EV5处。
3、有时什么都不做,直接初始化完了,进行发送第一个字节的时候,也会莫名其妙的挂在等待总线空闲处。(间歇性的,时好时坏)。
补充一下(FM24CL04:4K的内存,分两页,(从机地址的倒数第二位区分操作的是哪个页),一页2Kbit也就是256个字节:2Kbit=256*8bit).
以下是我的代码,请指正啊:
#include "config.h"
//PB5/I2C_WP 写保护 #define DIS_WP() GPIO_ResetBits(GPIOB,GPIO_Pin_5); // PB5/I2C_WP 低电平可写 #define EN_WP() GPIO_SetBits(GPIOB,GPIO_Pin_5); // PB5/I2C_WP 高电平写保护 /******************************************************** *函数名称:void I2C1_Init(uint32 fi2c) *函数功能: STM32 硬件I2C初始化,主程序要配置好I2C总线接口(I2C引脚功能,并已使能I2C主模式) *未开中断,之前的函数I2C收发都是在中断中完成的。 ********************************************************/ void I2C1_Init(uint32 fi2c) { /*PB6 I2C1_SCL PB7 I2C1_SDA */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD; //复用开漏输出 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); //PB5/I2C1_Wp GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;; //开漏输出 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); /******************** 配置I2C1为I2C主模式,置I2C 的占空比/快速模式Tlow / Thigh = 2, 置第一个设备自身地址 7位模式,使能应答(ACK),应答 7 位地址,时钟频率200KHz **********************/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); I2C_InitTypeDef I2C_InitStructure; I2C_DeInit(I2C1); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//配置I2C2为主模式 I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//快速模式Tlow / Thigh = 2 I2C_InitStructure.I2C_OwnAddress1 = 0x30;//第一个设备自身地址0x30; 7位模式 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//使能应答(ACK) I2C_InitStructure.I2C_AcknowledgedAddress =I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = fi2c;//时钟频率200KHz I2C_Init(I2C1, &I2C_InitStructure);//初始化I2C1 // I2C_ITConfig(I2C1, I2C_IT_BUF | I2C_IT_EVT, ENABLE);//使能缓存和事件中断 I2C_Cmd(I2C1, ENABLE);//使能I2C1
DS_WP(); //禁止写保护
}
/******************************************************* ** 函数名称:ISendByte() 从机地址内包含页地址1位,读写1位 ** 函数功能:向无子地址器件发送1字节数据。 ** 入口参数:sla 要写入数据的地址 器件地址 ** dat 要写入的数据 要发送的数据 ***************************************************/ uint8 ISendByte(uint8 sla, uint8 dat) { I2C_GenerateSTART(I2C1,ENABLE);//发送一个开始讯号 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)){;}//等待EV5 已发送起始条件 I2C_Send7bitAddress(I2C1, 0xA2, I2C_Direction_Transmitter);//发送从机地址 写 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){;}//等待EV6 已发送从机地址 I2C_SendData(I2C1, sla);//发送要写的寄存器地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8 第一个数据已发送 I2C_SendData(I2C1, dat);//发送要写入到从机寄存器地址中的数据 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8 I2C_GenerateSTOP(I2C1,ENABLE); //停止位 return 1;//无实意,防止系统警告 13 12 4 } /****************************************************** ** 函数名称:IRcvByte() 从机地址内包含页地址1位,读写1位 ** 入口参数:sla 器件地址 ** dat 接收数据的变量指针 ** 出口参数:返回值为0时表示操作出错,为1时表示操作正确。 ** 说明:使用前要初始化好I2C引脚功能,并已使能I2C主模式 ***************************************************/ uint8 IRcvByte(uint8 sla, uint8 *dat) { I2C_AcknowledgeConfig(I2C1, ENABLE); //使能应答位 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //等待I2C I2C_GenerateSTART(I2C1, ENABLE); //产生起始信号 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //等待EV5 I2C_Send7bitAddress(I2C1,0xA2, I2C_Direction_Transmitter); //发送从机地址 写 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//EV6 I2C_SendData(I2C1, sla); //发送读地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8 I2C_GenerateSTART(I2C1, ENABLE); //重新启动 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//EV5 I2C_Send7bitAddress(I2C1,0xA2, I2C_Direction_Receiver);//发送从机地址 读 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//EV6 I2C_AcknowledgeConfig(I2C1, DISABLE); //关闭应答和停止条件产生 I2C_GenerateSTOP(I2C1, ENABLE); //停止条件产生 while(!(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))); //EV7 *(dat++)=I2C_ReceiveData(I2C1); return 1; } /************************************************ ** 函数名称 :I2C_ReadNByte() 从机地址内包含页地址1位,读写1位 ** 函数功能 :从有子地址器件任意地址开始读取N字节数据 ** 入口参数 : sla 器件从地址 ** suba 器件子地址 ** s 数据接收缓冲区指针 ** num 读取的个数 ********************************************************/ uint8 I2C_ReadNByte (uint8 sla, uint32 suba_type, uint32 suba, uint8 *s, uint32 num) { suba_type=suba_type;//防止系统报错 I2C_AcknowledgeConfig(I2C1,ENABLE);//使能应答位 while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)); //等待I2C空闲 I2C_GenerateSTART(I2C1,ENABLE);//产生起始信号 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//等待EV5 ///////////////////发送从机地址 写 I2C_Send7bitAddress(I2C1,sla, I2C_Direction_Transmitter); //发送从机地址 写 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//EV6 //////////发送要读取的数据的地址 I2C_SendData(I2C1, suba); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8 I2C_GenerateSTART(I2C1, ENABLE);//重新发送 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//EV5 ////////////////////////////发送从机地址 读 I2C_Send7bitAddress(I2C1, sla, I2C_Direction_Receiver);//发送从机地址 读 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//EV6 while(num) { if(num==1) //写255的时候,执行完if后会挂在while(),写20个时没事啊 { I2C_AcknowledgeConfig(I2C1, DISABLE);//关闭应答 I2C_GenerateSTOP(I2C1, ENABLE);//关闭应答和停止条件产生 OK } while(!(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)));//等待EV7 *(s++)=I2C_ReceiveData(I2C1); num--; } I2C_AcknowledgeConfig(I2C1, ENABLE);//使能i2C 指定的应答功能 return 1;//无实意,防止警告 } /************************************************ ** 函数名称 :I2C_WriteNByte() 从机地址内包含页地址1位,读写1位 ** 函数功能 :向有子地址器件写入N字节数据 ** 入口参数 : sla 器件从地址(区别两个页0XA0对应1页,0XA2对应0页) ** suba_type 子地址结构 1-单字节地址 3-8+X结构 2-双字节地址 ** suba 器件内部物理地址 ** *s 将要写入的数据的指针 ** num 将要写入的数据的个数 ****************************************************/ uint8 I2C_WriteNByte(uint8 sla, uint8 suba_type, uint32 suba, uint8 *s, uint32 num) { //此处有两个问题1、 写超过一页。2、写的内存地址(uint32) suba_type=suba_type; //防止报错 子地址结构 suba=(uint8)(suba); //要写入的数据的地址 char型 while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)); //等待I2C空闲 I2C_GenerateSTART(I2C1,ENABLE); //产生I2C1传输START条件 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)){;}//等待EV5 I2C_Send7bitAddress(I2C1, sla, I2C_Direction_Transmitter);//发送从机地址 写 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){;}//等待EV6 I2C_SendData(I2C1, suba);//发送要写的寄存器地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8 while(num) { I2C_SendData(I2C1, *s++);//发送要写入到从机寄存器地址中的数据 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8 num--; } I2C_GenerateSTOP(I2C1,ENABLE); return 1;//无实意,防止系统警告 } /////////测试任务 I2C FM24CL04 static void I2CFlash_task(void *p_arg) { p_arg=p_arg; I2C1_Init(200000); //FM24cl04的I2C总线初始化 速率200K SysDOInit(); //测试任务切换用 开关量输出管脚初始化 unsigned int i=0; uint8 AddrI2C[256],add; //建立数组用来存储要读、写的数据的地址 uint8 Readdata_AddrI2C[256]={0};//存储读取来的数据 uint8 onedata[256]={0}; //////初始化要读取的数据的地址数组 for(i=0;i<=255;i++) { AddrI2C[i]=i++; //初始化要读取的数据的地址数组 } while(1) { CtrlDOData(0x80); //测试任务切换用点灯 ///////////////////测试写单个字节 Begin 向24CL04发送256个数据0~~255 //目前数据发多了(目前测试120)就会进入硬件故障,不知道哪里出了问题?超限了? for(i=0;i<=110;i++) { ISendByte(AddrI2C[i],i); //向24CL04发送256个数据0~~255 IRcvByte(AddrI2C[i],&onedata[i]); //从24CL04读取256个数据0~~255存入onedata[]中 } ///////////////////测试写单个字节 end /////////////////////////测试写多个字节 begin add=0x00; //重新赋0地址 uint8 AddrI2C1[256]= {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA}; I2C_WriteNByte(0xA2, 1, add, AddrI2C1, 20); //写多个字节数据到FM24CL04 I2C_ReadNByte(0xA2,1, add,Readdata_AddrI2C,20);//从FM24CL04读多个字节数据 /////////////////////////测试写多个字节 end OSTimeDlyHMSM(0,0,0,50); } }