看了半天还是不会写,只能反复看例程找到规律了。因为手上stm32的例程非常完整,还包括了对将要用到的无线通信模块和mpu6050模块的具体使用,所以就从这里入手开始学习,完了再仔细看看msp430的串口相关的例程,希望能比较顺利吧。。。
一、IIC对24cxx的读写
首先是模拟IIC的源文件,跟以前一模一样的。
然后对24cxx模块的读取的头文件是这样的:
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和模块的读写协议,实现读写功能。
1、读一个字节
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));
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;
}
24c02是空间为256个字节的存储器,读时序如下:
即 起始信号——发器件地址+写指令(可以理解为是一个写控制字)——发字节地址——起始信号——发器件地址+读指令(可以理解为一个读控制字)——接收数据——发送非应答——发结束信号
但是看代码,会发现有分支操作,如果器件空间大于2048,即11位可表示的地址时,会先发0xA0,再发高地址,再发低地址,如果器件地址小于等于11位,
则会发0xA+高三位地址+0/1,0/1就是读写位,再发低地址,每发一次都要等待应答,然后发起始信号,然后发0XA1,进入接收模式,等待应答,然后开始读操作并发送非应答信号,然后发终止信号,将读到的字节返回,就完了。
2、写一个字节
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));
}
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256);
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);
}
写时序如下图:
起始信号——器件地址+0(写控制字)——发字节地址——发数据——终止信号
相对简单一点,发地址和写指令的操作跟上面是一样的,写完地址可以进接着写数据进去,然后发送终止信号,就没了。
3、读写指定长度的字节
这个就比较简单,重复以上操作就可以,不说了
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);
}
}
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;
}
4、进行指定个数量的字节的读写
也比较简单,就是循环调用读单个字节的函数就可以。
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
5、在使用中,可以直接把字符串写进去,因为一个字符实际上是一个八位ascii码,保存在存储器中,读取出来利用显示模块可以直接将字符显示出来。
二、利用SPI与W25Q128进行通信
这个就比24cxx复杂一些,光指令表就一大串,虽然会用到的可能不会很多,但总之就是看起来很厉害。。。
spi部分一样,没什么内容,重点还是怎么结合通信协议编写源文件。
头文件是这样的:
void W25QXX_Init(void);
u16 W25QXX_ReadID(void);
u8 W25QXX_ReadSR(void);
void W25QXX_Write_SR(u8 sr);
void W25QXX_Write_Enable(void);
void W25QXX_Write_Disable(void);
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Erase_Chip(void);
void W25QXX_Erase_Sector(u32 Dst_Addr);
void W25QXX_Wait_Busy(void);
void W25QXX_PowerDown(void);
void W25QXX_WAKEUP(void);
看的脑壳疼。。。。看模块介绍:
每个sector是4K,也就是4096个地址, 在写任何一个地址之前,如果该地址的值不是0xFF,必须先擦除对应的sector,然后再写。
根据要写的起始地址,确定要写的起始区域的Sector号以及在起始 sector中的偏移量。
根据要写的起始地址和字节数,确定要写的数据是否跨sector。 确定好要操作的sector以及sector的地址范围。
对每一个sector,先遍历要写的地址区域保存的数据是不是0xff,如果都是,就不用擦除。如果有不是0xff的区域,先读出里面的数据,保存在缓存W25QXX_BUFFER,然后擦除里面的内容。然后把这个sector要操作的数据,写到缓存。最后一次性吧缓存W25QXX_BUFFER的数据写到这个对应的sector。
1、先readID,就是读器件型号
u16 W25QXX_ReadID(void)
{
u16 Temp = 0;
W25QXX_CS=0;
SPI2_ReadWriteByte(0x90); //发送读取ID命令
SPI2_ReadWriteByte(0x00);
SPI2_ReadWriteByte(0x00);
SPI2_ReadWriteByte(0x00);
Temp|=SPI2_ReadWriteByte(0xFF)<<8;
Temp|=SPI2_ReadWriteByte(0xFF);
W25QXX_CS=1;
return Temp;
}
发送读取ID的控制字,然后交换三个字节之后进行数据读取。
2、
读取器件的状态寄存器
u8 W25QXX_ReadSR(void)
{
u8 byte=0;
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_ReadStatusReg); //读取状态寄存器控制字
byte=SPI2_ReadWriteByte(0Xff);
W25QXX_CS=1;
return byte;
}
这个却不需要交换三个空字节,可能这个比较特殊。
写状态寄存器
void W25QXX_Write_SR(u8 sr)
{
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_WriteStatusReg); //相应的控制字
SPI2_ReadWriteByte(sr);
W25QXX_CS=1;
}
也不需要交换空字节。
3、
w25Qxx写使能:
void W25QXX_Write_Enable(void)
{
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_WriteEnable); //相应的控制字
W25QXX_CS=1;
}
写禁止:
void W25QXX_Write_Disable(void)
{
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_WriteDisable);
W25QXX_CS=1;
}
仍然不需要交换空字节,难道第一个才比较特殊?。。。
4、从指定地址读取指定长度的字节
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_ReadData); //发送读取命令
SPI2_ReadWriteByte((u8)((ReadAddr)>>16)); //高8位地址
SPI2_ReadWriteByte((u8)((ReadAddr)>>8)); //中8位地址
SPI2_ReadWriteByte((u8)ReadAddr); //低8位地址,总共发了24位地址
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPI2_ReadWriteByte(0XFF);
}
W25QXX_CS=1;
}
发完控制字,接着三次发送是地址,再接着是数据。器件内部数据指针会自动++。
5、SPI在一页(0~65536)内写入少于256个字节的数据
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
W25QXX_Write_Enable();
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_PageProgram);
SPI2_ReadWriteByte((u8)((WriteAddr)>>16));
SPI2_ReadWriteByte((u8)((WriteAddr)>>8));
SPI2_ReadWriteByte((u8)WriteAddr);
for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer[i]);
W25QXX_CS=1;
W25QXX_Wait_Busy();
}
同样,传入控制字,然后三次地址传输,接着不断向缓冲器写入数据,期间内部地址指针会自动++的。
6、无检验写flash,在指定地址开始写入数据,具有自动换页功能。
必须确保所写地址内全部为0xFF,否则会写入失败。
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 pageremain;
pageremain=256-WriteAddr%256;
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;
while(1)
{
W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break;
else
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain;
if(NumByteToWrite>256)pageremain=256;
else pageremain=NumByteToWrite;
}
};
}
逻辑不是很直接,但多看两遍就看明白了,不说了。。。
7、擦除
擦除整个芯片:
void W25QXX_Erase_Chip(void)
{
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_ChipErase);
W25QXX_CS=1;
W25QXX_Wait_Busy();
}
擦除一个扇区:
void W25QXX_Erase_Sector(u32 Dst_Addr)
{
printf("fe:%x\r\n",Dst_Addr);
Dst_Addr*=4096;
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_SectorErase);
SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));
SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));
SPI2_ReadWriteByte((u8)Dst_Addr);
W25QXX_CS=1;
W25QXX_Wait_Busy();
}
写控制字,写地址(三个),然后就自动擦了。。。
8、写入指定长度的数据
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 * W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;
secoff=WriteAddr%4096;
secremain=4096-secoff;
//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;
while(1)
{
W25QXX_Read(W25QXX_BUF,secpos*4096,4096);
for(i=0;i<secremain;i++)
{
if(W25QXX_BUF[secoff+i]!=0XFF)break;
}
if(i<secremain)
{
W25QXX_Erase_Sector(secpos);
for(i=0;i<secremain;i++)
{
W25QXX_BUF[i+secoff]=pBuffer[i];
}
W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);
}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);
if(NumByteToWrite==secremain)break;
else
{
secpos++;
secoff=0;
pBuffer+=secremain;
WriteAddr+=secremain;
NumByteToWrite-=secremain;
if(NumByteToWrite>4096)secremain=4096;
else secremain=NumByteToWrite;
}
};
}
这个是最头大的。。。大概就是把要写的区域读出来,有非0xFF的就把这个扇区清空,然后写入数据,最后就是把该写的都写进去。。。不说了,很头大。
先到这里,晚上看那两个更加让人头大的模块。。。