IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 嵌入式第三课(SPI篇) -> 正文阅读

[嵌入式]嵌入式第三课(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);//éè???μí3?D??ó??è??·?×é2
	delay_init(168);     //3?ê??ˉ?óê±oˉêy
	uart_init(115200);	//3?ê??ˉ′??ú2¨ì??ê?a115200
	LED_Init();					//3?ê??ˉLED 
 	LCD_Init();					//LCD3?ê??ˉ 
	KEY_Init(); 				//°′?ü3?ê??ˉ  
	W25QXX_Init();			//W25QXX3?ê??ˉ
 	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");	//??ê?ìáê?D??¢		
	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 ′óD??a16×??ú
  	POINT_COLOR=BLUE;			//éè??×?ì??aà?é?	  
	while(1)
	{
		key=KEY_Scan(0);
		if(key==KEY1_PRES)//KEY1°′??,D′è?W25Q128
		{
			LCD_Fill(0,170,239,319,WHITE);//??3y°??á    
 			LCD_ShowString(30,170,200,16,16,"Start Write W25Q128....");
			W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);		//′óμ1êyμú100??μ??·′|?aê?,D′è?SIZE3¤?èμ?êy?Y
			LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!");	//ìáê?′??ííê3é
		}
		if(key==KEY0_PRES)//KEY0°′??,?áè?×?·?′?2¢??ê?
		{
 			LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");
			W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);					//′óμ1êyμú100??μ??·′|?aê?,?á3?SIZE??×??ú
			LCD_ShowString(30,170,200,16,16,"The Data Readed Is:   ");	//ìáê?′??ííê3é
			LCD_ShowString(30,190,200,16,16,datatemp);					//??ê??áμ?μ?×?·?′?
		} 
		i++;
		delay_ms(10);
		if(i==20)
		{
			LED0=!LED0;//ìáê??μí3?y?ú??DD	
			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);//ê1?üGPIOBê±?ó
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//ê1?üSPI1ê±?ó
 
  //GPIOFB3,4,53?ê??ˉéè??
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5?′ó?1|?üê?3?	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//?′ó?1|?ü
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//í?íìê?3?
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//é?à-
  GPIO_Init(GPIOB, &GPIO_InitStructure);//3?ê??ˉ
	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3?′ó??a SPI1
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4?′ó??a SPI1
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5?′ó??a SPI1
 
	//?aà???????SPI?ú3?ê??ˉ
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//?′??SPI1
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//í£?1?′??SPI1

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //éè??SPIμ¥?ò?ò?????òμ?êy?Y?£ê?:SPIéè???a???????òè???1¤
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//éè??SPI1¤×÷?£ê?:éè???a?÷SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//éè??SPIμ?êy?Y′óD?:SPI·¢?í?óê?8?????á11
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//′?DDí?2?ê±?óμ????D×′ì??a??μ???
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//′?DDí?2?ê±?óμ?μú?t??ì?±???£¨é?éy?ò???μ£?êy?Y±?2é?ù
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSSD?o?óéó2?t£¨NSS1ü??£??1ê?èí?t£¨ê1ó?SSI??£?1üàí:?ú2?NSSD?o?óDSSI??????
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//?¨ò?2¨ì??ê?¤·??μμ??μ:2¨ì??ê?¤·??μ?μ?a256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//???¨êy?Y′?ê?′óMSB???1ê?LSB???aê?:êy?Y′?ê?′óMSB???aê?
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC?μ????μ??à??ê?
	SPI_Init(SPI1, &SPI_InitStructure);  //?ù?YSPI_InitStruct?D???¨μ?2?êy3?ê??ˉíaéèSPIx??′??÷
 
	SPI_Cmd(SPI1, ENABLE); //ê1?üSPIíaéè

	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){}//μè′y·¢?í????  
	
	SPI_I2S_SendData(SPI1, TxData); //í¨1yíaéèSPIx·¢?íò???byte  êy?Y
		
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //μè′y?óê?íêò???byte  
 
	return SPI_I2S_ReceiveData(SPI1); //·μ??í¨1ySPIx×??ü?óê?μ?êy?Y	
 		    
}

因为我这块芯片需要发送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,很头疼!!!

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 02:09:57  更:2022-09-15 02:12:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 18:08:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计