从这里开始,我将真实记录我所做项目的一点一滴,从开始,到解决困难。
连续写了两篇博客,终于可以写这个项目是怎样开始的了,前面的都是基础,是大学四年欠下的债,呜呜。。。
1.项目启动(SPI初始化)
首先这个项目所用的主控也就是SPI的Master是STM32F4探索者,那我就需要准备STM32F4的数据手册,以及正点原子官方提供的SPI实验例程,先看数据手册:
从这里可以看出SPI的三根线分别是PB3,4,5,至于第四根线,CS片选线,之后会提到。
程序源码选择的是正点原子提供的标准例程—库函数版本的实验25 SPI实验:
int main(void)
{
u8 key;
u16 i=0;
u8 datatemp[SIZE];
u32 FLASH_SIZE;
u16 id = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÉèÖÃϵͳÖжÏÓÅÏȼ¶·Ö×é2
delay_init(168); //³õʼ»¯ÑÓʱº¯Êý
uart_init(115200); //³õʼ»¯´®¿Ú²¨ÌØÂÊΪ115200
LED_Init(); //³õʼ»¯LED
LCD_Init(); //LCD³õʼ»¯
KEY_Init(); //°´¼ü³õʼ»¯
W25QXX_Init(); //W25QXX³õʼ»¯
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"SPI TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2014/5/6");
LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read"); //ÏÔʾÌáʾÐÅÏ¢
while(1)
{
id = W25QXX_ReadID();
if (id == W25Q128 || id == NM25Q128)
break;
LCD_ShowString(30,150,200,16,16,"W25Q128 Check Failed!");
delay_ms(500);
LCD_ShowString(30,150,200,16,16,"Please Check! ");
delay_ms(500);
LED0=!LED0; //DS0ÉÁ˸
}
LCD_ShowString(30,150,200,16,16,"W25Q128 Ready!");
FLASH_SIZE=16*1024*1024; //FLASH ´óСΪ16×Ö½Ú
POINT_COLOR=BLUE; //ÉèÖÃ×ÖÌåΪÀ¶É«
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES)//KEY1°´ÏÂ,дÈëW25Q128
{
LCD_Fill(0,170,239,319,WHITE);//Çå³ý°ëÆÁ
LCD_ShowString(30,170,200,16,16,"Start Write W25Q128....");
W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE); //´Óµ¹ÊýµÚ100¸öµØÖ·´¦¿ªÊ¼,дÈëSIZE³¤¶ÈµÄÊý¾Ý
LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!"); //Ìáʾ´«ËÍÍê³É
}
if(key==KEY0_PRES)//KEY0°´ÏÂ,¶ÁÈ¡×Ö·û´®²¢ÏÔʾ
{
LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");
W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE); //´Óµ¹ÊýµÚ100¸öµØÖ·´¦¿ªÊ¼,¶Á³öSIZE¸ö×Ö½Ú
LCD_ShowString(30,170,200,16,16,"The Data Readed Is: "); //Ìáʾ´«ËÍÍê³É
LCD_ShowString(30,190,200,16,16,datatemp); //ÏÔʾ¶Áµ½µÄ×Ö·û´®
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//ÌáʾϵͳÕýÔÚÔËÐÐ
i=0;
}
}
}
在这里我把main.c所有的内容都拿来了,就是为了说明一点,看别人写好的程序的时候,从哪里入手,因为我们要用的是SPI,那首先需要一个初始化吧,看了他所有的初始化,没发现有类似SPI_Init()字样,所以我一个一个点进去看,发现W25QXX_Init()里面,调用了SPI1_Init(),这不就成了吗,先不看别的,直接点进SPI1_Init(),看他是怎样定义以及使用的。
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//ʹÄÜGPIOBʱÖÓ
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//ʹÄÜSPI1ʱÖÓ
//GPIOFB3,4,5³õʼ»¯ÉèÖÃ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5¸´Óù¦ÄÜÊä³ö
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//¸´Óù¦ÄÜ
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);//³õʼ»¯
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3¸´ÓÃΪ SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4¸´ÓÃΪ SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5¸´ÓÃΪ SPI1
//ÕâÀïÖ»Õë¶ÔSPI¿Ú³õʼ»¯
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//¸´Î»SPI1
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//Í£Ö¹¸´Î»SPI1
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //ÉèÖÃSPIµ¥Ïò»òÕßË«ÏòµÄÊý¾Ýģʽ:SPIÉèÖÃΪ˫ÏßË«ÏòÈ«Ë«¹¤
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //ÉèÖÃSPI¹¤×÷ģʽ:ÉèÖÃΪÖ÷SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //ÉèÖÃSPIµÄÊý¾Ý´óС:SPI·¢ËͽÓÊÕ8λ֡½á¹¹
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //´®ÐÐͬ²½Ê±ÖӵĿÕÏÐ״̬Ϊ¸ßµçƽ
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //´®ÐÐͬ²½Ê±Öӵĵڶþ¸öÌø±äÑØ£¨ÉÏÉý»òϽµ£©Êý¾Ý±»²ÉÑù
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSSÐźÅÓÉÓ²¼þ£¨NSS¹Ü½Å£©»¹ÊÇÈí¼þ£¨Ê¹ÓÃSSI룩¹ÜÀí:ÄÚ²¿NSSÐźÅÓÐSSIλ¿ØÖÆ
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //¶¨Ò岨ÌØÂÊÔ¤·ÖƵµÄÖµ:²¨ÌØÂÊÔ¤·ÖƵֵΪ256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //Ö¸¶¨Êý¾Ý´«Êä´ÓMSBλ»¹ÊÇLSBλ¿ªÊ¼:Êý¾Ý´«Êä´ÓMSBλ¿ªÊ¼
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRCÖµ¼ÆËãµÄ¶àÏîʽ
SPI_Init(SPI1, &SPI_InitStructure); //¸ù¾ÝSPI_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèSPIx¼Ä´æÆ÷
SPI_Cmd(SPI1, ENABLE); //ʹÄÜSPIÍâÉè
SPI1_ReadWriteByte(0xff);//Æô¶¯´«Êä
}
从这个SPI的初始化中可以看出,和最开始原理图中的一样,他将PB3,4,5复用为SPI1,然后是配置主模式,全双工,每次发送和接收的数据大小为8位(一个字节)。
然后配置时钟相位极性,也就是重要的SPI时序。
接着是配置NSS信号由软件控制还是硬件控制,在这里我纠结了好久,不懂为什么要区分软硬件控制,那既然提到这个,就浅讲一下:
NSS_Soft or NSS_Hard
SPI_NSS_SOFT :当SPI配置为在Master的时候,NSS作为普通IO,由用户自己写代码控制片选,可以1主多从。
SPI_NSS_HARD :当SPI配置为在Master的时候,NSS作为SPI专用IO,由MCU自动控制片选,只能1主1从。
在后面很长一段时间,代码只要是没跑通,我就会怀疑这里是不是有问题,设置了各种的NSS高或者低,都没有用,其实如果是一主一从,这里根本不用管,STM32F4上面SPI1_NSS是PA15,但根本就不需要配置这个引脚。
然后就是设置预分频,高位优先(MSB)还是低位优先(LSB)
最后CRCPolynomial = 7这个是默认的,不用管!不用管!
2.SPI发送接收
SPI发送和接收分两种,这也是我后来才了解到的,正点原子的官方例程中给出的是最常见的一种,收发同时进行:
u8 SPI1_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//µÈ´ý·¢ËÍÇø¿Õ
SPI_I2S_SendData(SPI1, TxData); //ͨ¹ýÍâÉèSPIx·¢ËÍÒ»¸öbyte Êý¾Ý
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //µÈ´ý½ÓÊÕÍêÒ»¸öbyte
return SPI_I2S_ReceiveData(SPI1); //·µ»Øͨ¹ýSPIx×î½ü½ÓÊÕµÄÊý¾Ý
}
因为我这块芯片需要发送7字节的指令,然后接收12字节的应答,所以定义三个数组Tx1_buf,Tx2_buf和Rx_buf.
Tx1_buf存放发送的命令,Tx2_buf初始化为12个0xff,由Master发送给Slave来换取应该接收到的应答,Rx_buf用来接收Slave返回的应答。
u8 test_tx1_buf[7] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07};
u8 test_tx2_buf[12] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
for(j=0;j<7;j++)
SPI1_ReadWriteByte(test_tx1_buf[j]);
for(j=0;j<12;j++)
{
test_rx1_buf[j] = SPI1_ReadWriteByte(test_tx2_buf[j]);
}
for(j=1;j<12;j++)
printf("%c",test_rx1_buf[j]);
代码写到这基本上就差不多了,我心想这不就成了吗,SPI就这?
3.SPI时序和速率的问题
结果通过串口打印出来是12个FF,显然结果是错的,然后就进入了漫长的找问题过程。。。。。
看了半天程序都看不出来有什么问题,决定在时序这个位置找找原因,反正CPOL和CPHA两两组合就只有四种模式,所以就分别都试了一次,发现还是12个FF。
然后又发现在初始化时有预分频SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
STM32F4的时钟主频在168MHZ,256分频后速率只有0.65MHZ,也许是速率太慢导致的,芯片手册上说主设备最高串行时钟频率建议不超过6MHZ,于是设置了32分频,SPI1_SetSpeed(SPI_BaudRatePrescaler_32);
想着把速率提上来或许就可以通信,结果还是12个FF,很头疼!!!