从这里开始,我将真实记录我所做项目的一点一滴,从开始,到解决困难。 连续写了两篇博客,终于可以写这个项目是怎样开始的了,前面的都是基础,是大学四年欠下的债,呜呜。。。
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);
delay_init(168);
uart_init(115200);
LED_Init();
LCD_Init();
KEY_Init();
W25QXX_Init();
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;
}
LCD_ShowString(30,150,200,16,16,"W25Q128 Ready!");
FLASH_SIZE=16*1024*1024;
POINT_COLOR=BLUE;
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES)
{
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);
LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!");
}
if(key==KEY0_PRES)
{
LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");
W25QXX_Read(datatemp,FLASH_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);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
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);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){}
return SPI_I2S_ReceiveData(SPI1);
}
因为我这块芯片需要发送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,很头疼!!!
|