我是靠谱客的博主 标致帆布鞋,最近开发中收集的这篇文章主要介绍嵌入式第三课(SPI篇),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

从这里开始,我将真实记录我所做项目的一点一滴,从开始,到解决困难。
连续写了两篇博客,终于可以写这个项目是怎样开始的了,前面的都是基础,是大学四年欠下的债,呜呜。。。

我与SPI的故事

      • 1.项目启动(SPI初始化)
        • NSS_Soft or NSS_Hard
      • 2.SPI发送接收
      • 3.SPI时序和速率的问题

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,很头疼!!!

最后

以上就是标致帆布鞋为你收集整理的嵌入式第三课(SPI篇)的全部内容,希望文章能够帮你解决嵌入式第三课(SPI篇)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部