STM32 SPI接口的简单实现

发布者:EnchantingEyes最新更新时间:2021-04-29 来源: eefocus关键字:STM32  SPI接口 手机看文章 扫描二维码
随时随地手机看文章

通常SPI通过4个引脚与外部器件相连:


● MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。


● MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。


● SCK:串口时钟,作为主设备的输出,从设备的输入


●NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。一旦被使能(SSOE位),NSS引脚也可以作为输出引脚,并在SPI处于主模式时拉低;此时,所有的SPI设备,如果它们的NSS引脚连接到主设备的NSS引脚,则会检测到低电平,如果它们被设置为NSS硬件模式,就会自动进入从设备状态。当配置为主设备、NSS配置为输入引脚(MSTR=1,SSOE=0)时,如果NSS被拉低,则这个SPI设备进入主模式失败状态:即MSTR位被自动清除,此设备进入从模式。




时钟信号的相位和极性


SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0’,SCK引脚在空闲状态保持低电平;如果CPOL被置’1’,SCK引脚在空闲状态保持高电平。


如果CPHA(时钟相位)位被置’1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。如果CPHA位被清’0’,SCK时钟的第一边沿(CPOL位为’0’时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位采样,数据在第一个时钟边沿被锁存。


CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。

图212显示了SPI传输的4种CPHA和CPOL位组合。此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。


STM32 SPI接口的简单实现 - java - stm32学习日志

CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。

上图显示了SPI传输的4种CPHA和CPOL位组合。此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。

注意:


1. 在改变CPOL/CPHA位之前,必须清除SPE位将SPI禁止。

2. 主和从必须配置成相同的时序模式。

3.SCK的空闲状态必须和SPI_CR1寄存器指定的极性一致(CPOL为’1’时,空闲时应上拉SCK为高电平;CPOL为’0’时,空闲时应下拉SCK为低电平)。

4. 数据帧格式(8位或16位)由SPI_CR1寄存器的DFF位选择,并且决定发送/接收的数据长度。


我只要知道主机和从机的CPOL和CPHA位要一致就够了。




有2种NSS模式:

●软件NSS模式:可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动

● 硬件NSS模式,分两种情况:

─NSS输出被使能:当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。当一个SPI设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备;如果它不能拉低NSS,这意味着总线上有另外一个主设备在通信,这时将产生一个硬件失败错误(HardFault)。

─ NSS输出被关闭:允许操作于多主环境。

//我们用软件NSS主从的转换都可借助库来实现


数据帧格式

根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以MSB在先也可以LSB在先。

根据SPI_CR1寄存器的DFF位,每个数据帧可以是8位或是16位。所选择的数据帧格式对发送和/或接收都有效。


配置一个SPI 这里选SPI2


如下:


SPI_InitTypeDef SPI_InitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);


SPI_Cmd(SPI2, DISABLE);//必须先禁能,才能改变MODE

SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//两线全双工

SPI_InitStructure.SPI_Mode =SPI_Mode_Master;//主

SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;//8位

SPI_InitStructure.SPI_CPOL =SPI_CPOL_High;//CPOL=1时钟悬空高

SPI_InitStructure.SPI_CPHA =SPI_CPHA_1Edge;//CPHA=1 数据捕获第2个

SPI_InitStructure.SPI_NSS =SPI_NSS_Soft;//软件NSS

SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_2;//2分频

SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB;//高位在前

SPI_InitStructure.SPI_CRCPolynomial =7;//CRC7


SPI_Init(SPI2,&SPI_InitStructure);

SPI_Cmd(SPI2, ENABLE);


//spi的配置结束了可以使用了。


也可用 函数SPI_StructInit 把SPI_InitStruct中的每一个参数按缺省值填入


发送缓冲器空闲标志(TXE)【3.0SPI_I2S_FLAG_TXE】

此标志为’1’时表明发送缓冲器为空,可以写下一个待发送的数据进入缓冲器中。当写入SPI_DR时,TXE标志被清除。

接收缓冲器非空(RXNE)【3.0SPI_I2S_FLAG_RXNE】

此标志为’1’时表明在接收缓冲器中包含有效的接收数据。读SPI数据寄存器可以清除此标志。


注意在2.0的库中函数 SPI_SendData SPI_ReceiveDataSPI_GetFlagStatus 等在3.0的库中 变为


SPI_I2S_SendDataSPI_I2S_ReceiveData SPI_I2S_GetFlagStatus


写一个发送/接受函数


static u8 SPIByte(u8 byte)


{

while((SPI2->SR &SPI_I2S_FLAG_TXE)==RESET);

//while((SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE))==RESET);

SPI2->DR = byte;

//SPI_I2S_SendData(SPI2,byte);

while((SPI2->SR &SPI_I2S_FLAG_RXNE)==RESET);


//while((SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE))==RESET);

return(SPI2->DR);


//returnSPI_I2S_ReceiveData(SPI2);读寄存器用硬件清除标志位。

//SPI_I2S_ClearFlag(SPI2,SPI_I2S_FLAG_RXNE) ;直接软件清除标志位。

}


这里有两种写法直接操作寄存器与用库函数,相对来说直接操作寄存器应该更直观一些。


关键字:STM32  SPI接口 引用地址:STM32 SPI接口的简单实现

上一篇:STM32的sd卡的IAP升级固件教程
下一篇:STM32外部中断详解

推荐阅读最新更新时间:2024-11-08 04:08

频率测量方法及STM32微控制器代码示例介绍
频率测量是在电子和通信领域中非常重要的任务,用于确定信号的周期性和事件的发生率。 在本文中,我们将介绍两种常用的频率测量方法:计数法和周期法,并提供与STM32微控制器的示例代码,以帮助你在实际应用中进行频率测量。 计数法 计数法是最简单的频率测量方法之一,它通过直接计数事件发生的次数,并与时间相关联来计算频率。 其原理如下: 首先,我们选择一个时间窗口,通常使用计时器来测量。 时间窗口可以是任意合适的时间段,例如1秒。 在这个时间窗口内,我们记录事件发生的次数,这可以通过外部事件触发器、传感器或计数器来实现。 最后,我们使用以下公式计算频率: 计数法的优点是简单易懂,适用于大多数应用场景。对于STM32
[单片机]
stm32 PWM驱动舵机
今天使用stm32控制舵机,调试了许久,总结如下: 注意事项: 1.PWM周期的设定:初值,20000-1,分频=72-1。周期是:72M/72*20000=20ms 频率=50hz; 2.这个型号的舵机应该是5VPWM信号输出。而STM32是0-3.3,引起无法驱动。 更新:原来我上次无法驱动舵机是因为我把舵机的三条线搞错了,大写的尴尬 VCC——– 红 GND——– 黑 DATA——– 白 幸亏我潜意识里吧黑色的当成GND。。。。。。。。不然。。。。 故:以下程序成立。 #include pbdata.h void RCC_Configuration(void); void GPIO_Confi
[单片机]
STM32的SWD下载模式
之前一直用的JTAG下载,最近使用了下SWD模式。SW模式使用了5个引脚,NRST :复位 SWCLK:串行线时钟 SWDIO:串行线调试数据输入/输出,另外的为 GND 和VCC SWD模式的优势:SWD 模式比 JTAG 在高速模式下面更加可靠. 在大数据量的情况下面 JTAG 下载 程序会失败, 但是 SWD 发生的几率会小很多. 基本使用 JTAG 仿真模式的情况下是可以直接使用 SWD 模式的, 比较方便,并且引脚比较少,便于集成。
[单片机]
<font color='red'>STM32</font>的SWD下载模式
stm32入门笔记(二)printf不定向直接使用问题
我们很多时候可能直接调用 printf,然后系统会出现各种各样的问题。 那么究竟是为什么呢? 因为我们需要将这个东西实现在,我们必须定义printf输出的端口 int fputc(int ch, FILE *f) { while(!(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == SET)) { USART_SendData(USART1, (u8) ch); } return ch; } 这才是c里面的标准输出函数。 当然,我一直都在为keil没有代码补全而烦恼,然后经过同事的指点,我发现是有这个功能的。 configuration
[单片机]
<font color='red'>stm32</font>入门笔记(二)printf不定向直接使用问题
STM32之RTC使用
RTC使用说明 STM3f10x的RTC时能涉及到的寄存器有RCC,BKP和RTC这三个大类寄存器;其中RCC主要控制了实时时钟和备份区的电源使能和时钟使能;RTC模块和时钟配置系统的寄存器是在后备区域的(即BKP),通过BKP后备区域来存储RTC配置的数据可以让在系统复位或待机模式下唤醒后RTC里面配置的数据维持不变;为此备份区还得涉及一个寄存器PWR,电源管理寄存器,备份区的写保护位在PWR- CR的第八位。 由于整个RTC都是位于后备区,而且RTC的APB1总线和内核的APB1总线是独立的,所以在系统复位和唤醒时,RTC和BKP的那些时钟不用从新配置;他们只受Backup domain software reset这个位
[单片机]
<font color='red'>STM32</font>之RTC使用
STM32温度传感器DS18B20测试程序 可通过TFT显示屏显示
/* tanqi TFT*/ #include stm32f10x_lib.h #include tft.h #include DS18B20.h extern u8 presence; /************************************************************** 系统时钟初始化 ****************************************************************/ void RCC_Configuration(void) { ErrorStatus HSEStartUpStat
[单片机]
STM32读写24C02遇到的问题
这几天在弄I2C,读取24C02的数据。我默默的敲完了代码,然后仿真。 代码就下面两行,就是写一个字节,然后读取。 I2C_EE_BufferWrite( &write,100, 1); I2C_EE_BufferRead(&read,100, 1); 然后就是各种不对,经过跟踪发现程序挂在了读上面。 具体的发现读数据的时候,刚发送了起始信号后就死掉了。 百思不得其解。 然后用历程上的测试代码来试又正常。 和正常的程序对比了,也没问题。各种参数完全正确。 昨天搞了一天,总觉得是哪个地方我拼错了或是参数设错了。然鹅一直到晚上也没找到原因。 今天早上一想,是不是这写和读之间要加点延迟啊(24C0
[单片机]
<font color='red'>STM32</font>读写24C02遇到的问题
STM32的printf使用
#include //加入以下代码,支持printf函数,而不需要选择use MicroLIB #if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle; }; FILE __stdout; //定义_sys_exit()以避免使用半主机模式 void _sys_exit(int x) { x = x; } //重定义fputc函数 int fputc(int ch, FILE *f) { while((USART1- SR&0X40)==
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved