STM32经典学习资料——SPI
STM32之FATFS文件资料系统(SPI方式)笔记
STM32之FATFS文件系统(SPI方式)笔记BY:T7Date:20171202At:YSU_B307开发环境:uVision : V5.12.0.0 STM32F103V8T6库版本 : STM32F10x_StdPeriph_Lib_V3.5.0FATSF : ff13a工程版本:FATFS_V1 日期:20171130硬件连接:SPI1_CS -> PA4 SPI1_CLK -> PA5 SPI1_MISO -> PA6 SPI1_MOSI -> PA7工程功能:建立在SPI_SD的基础上,完成文件系统的初步接触。
一、FATFS文件系统1.使用开源的FAT文件系统模块,其源代码的获取从官网:目前最新版本是:ff13a2.解压后得到两个文件:其中,documents相当于STM32的固件库使用手册,介绍FATFS系统的函数使用方法,source 中则是需要用到的源代码。
因为FATFS使用SD卡,所以FATFS的基础是SD卡的正常读写,这里采用SPI模式。
二、STM32之SD卡_SPI模式1.硬件连接:SPI1_CS -> PA4 SPI1_CLK -> PA5 SPI1_MISO -> PA6 SPI1_MOSI -> PA72.SPI模式下STM32读写SD卡的工程结构在确定STM32使用SPI模式读写SD卡没有问题后,进入FATSF文件系统的实验,另源代码在文档最后。
三、FATSF文件系统移植1.配置工程环境1)STM32读写SD卡-SPI模式成功2)将解压后的ff13a整个文件夹赋值到工程目录下,如图:3)返回到MDK界面下,添加ff13a项目组,并把ff13a\source\目录下ff.c,diskio.c,ffunicode.c,ffsystem.c添加到项目组中,如下图:4)在Target Options的C++编译器选项中添加文件包含路径,如下图四、为FATSF文件系统添加底层驱动(一)在diskio.c中添加函数代码1.DSTATUS disk_status (BYTE pdrv); 添加完成后如下图2.DSTATUS disk_initialize (BYTE pdrv); 添加完成后如下图3.DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);4.DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);5.DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);6.DWORD get_fattime (void);注意:在diskio.c中 DEV_MMC的宏定义要为0,如下图(二)打开Ffconf.h函数1.改变FF_CODE_PAGE的值如下2.改变FF_USE_LFN的值如下五、Main主函数Main.c函数如下代码:#include "main.h"#define ONE_BLOCK 512#define TWO_BLOCK 1024uint8_t sd_RxBuf[TWO_BLOCK];//SD卡数据j接收缓存区uint8_t sd_TxBuf[TWO_BLOCK] = {0};//SD卡数据j接收缓存区FRESULT res; //读写文件的返回值FIL FileSyatemSrc,FileSystemDst; //文件系统结构体,包含文件指针等成员UINT br,bw; //Fil R/W countBYTE FileRxBuffer[ONE_BLOCK]; //FILE COPY BUFFER//BYTE TxFileBuffer[] = "This is the FATFS System!\r\n";BYTE TxFileBuffer[] = "中文文件系统实验!\r\n";static const char * FR_Table[]={"FR_OK:成功", /* (0) Succeeded */"FR_DISK_ERR:底层硬件错误", /* (1) A hard error occurred in the low level disk I/O layer */"FR_INT_ERR:断言失败", /* (2) Assertion failed */"FR_NOT_READY:物理驱动没有工作", /* (3) The physical drive cannot work */"FR_NO_FILE:文件不存在", /* (4) Could not find the file */"FR_NO_PATH:路径不存在", /* (5) Could not find the path */"FR_INVALID_NAME:无效文件名", /* (6) The path name format is invalid */"FR_DENIED:由于禁止访问或者目录已满访问被拒绝", /* (7) Access denied due to prohibited access or directory full */"FR_EXIST:由于访问被禁止访问被拒绝", /* (8) Access denied due to prohibited access */"FR_INVALID_OBJECT:文件或者目录对象无效", /* (9) The file/directory object is invalid */"FR_WRITE_PROTECTED:物理驱动被写保护", /* (10) The physical drive is write protected */"FR_INVALID_DRIVE:逻辑驱动号无效", /* (11) The logical drive number is invalid */"FR_NOT_ENABLED:卷中无工作区", /* (12) The volume has no work area */"FR_NO_FILESYSTEM:没有有效的FAT卷", /* (13) There is no valid FAT volume */"FR_MKFS_ABORTED:由于参数错误f_mkfs()被终止", /* (14) The f_mkfs() aborted due to any parameter error */"FR_TIMEOUT:在规定的时间无法获得访问卷的许可", /* (15) Could not get a grant to access the volume within defined period */ "FR_LOCKED:由于文件共享策略操作被拒绝", /* (16) The operation is rejected according to the file sharing policy */"FR_NOT_ENOUGH_CORE:无法分配长文件名工作区", /* (17) LFN working buffer could not be allocated */"FR_TOO_MANY_OPEN_FILES:当前打开的文件数大于_FS_SHARE", /* (18) Number of open files > _FS_SHARE */"FR_INVALID_PARAMETER:参数无效" /* (19) Given parameter is invalid */};int main(void){int i = 0;FATFS fs; //记录文件系统盘符信息的结构体LED_Init();USARTx_Init();/* 调用f_mount()创建一个工作区,另一个功能是调用了底层的disk_initialize()函数,进行SDIO借口的初始化 */res = f_mount(&fs, "0:", 1 );if (res != FR_OK){printf("挂载文件系统失败 (%s)\r\n", FR_Table[res]);}else{printf("挂载文件系统成功 (%s)\r\n", FR_Table[res]);}/* 调用f_open()函数在刚刚开辟的工作区的盘符0下打开一个名为Demo.TXT的文件,以创建新文件或写入的方式打开(参数"FA_CREATE_NEW | FA_WRITE"),如果不存在的话则创建这个文件。
STM32_深入浅出(新手必看)资料
STM32学前班教程之一:为什么是它经过几天的学习,基本掌握了STM32的调试环境和一些基本知识。
想拿出来与大家共享,笨教程本着最大限度简化删减STM32入门的过程的思想,会把我的整个入门前的工作推荐给大家。
就算是给网上的众多教程、笔记的一种补充吧,所以叫学前班教程。
其中涉及产品一律隐去来源和品牌,以防广告之嫌。
全部汉字内容为个人笔记。
所有相关参考资料也全部列出。
:lol教程会分几篇,因为太长啦。
今天先来说说为什么是它——我选择STM32的原因。
我对未来的规划是以功能性为主的,在功能和面积之间做以平衡是我的首要选择,而把运算放在第二位,这根我的专业有关系。
里面的运算其实并不复杂,在入门阶段想尽量减少所接触的东西。
不过说实话,对DSP的外设并和开发环境不满意,这是为什么STM32一出就转向的原因。
下面是我自己做过的两块DSP28的全功能最小系统板,在做这两块板子的过程中发现要想尽力缩小DSP的面积实在不容易(目前只能达到50mm×45mm,这还是没有其他器件的情况下),尤其是双电源的供电方式和1.9V的电源让人很头疼。
后来因为一个项目,接触了LPC2148并做了一块板子,发现小型的ARM7在外设够用的情况下其实很不错,于是开始搜集相关芯片资料,也同时对小面积的A VR和51都进行了大致的比较,这个时候发现了CortexM3的STM32,比2148拥有更丰富和灵活的外设,性能几乎是2148两倍(按照MIPS值计算)。
正好2148我还没上手,就直接转了这款STM32F103。
与2811相比较(核心1.8V供电情况下),135MHz×1MIPS。
现在用STM32F103,72MHz×1.25MIPS,性能是DSP的66%,STM32F103R型(64管脚)芯片面积只有2811的51%,STM32F103C型(48管脚)面积是2811的25%,最大功耗是DSP的20%,单片价格是DSP 的30%。
STM32SPI详解
STM32SPI详解1、SPI简介SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). ⼀个 Master 设备可以通过提供 Clock 以及对 Slave 设备进⾏⽚选 (Slave Select) 来控制多个 Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本⾝不能产⽣或控制 Clock, 没有 Clock 则 Slave 设备不能正常⼯作。
2、SPI特点2.1、SPI控制⽅式采⽤主-从模式(Master-Slave) 的控制⽅式。
SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). ⼀个 Master 设备可以通过提供 Clock 以及对 Slave 设备进⾏⽚选 (Slave Select) 来控制多个 Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本⾝不能产⽣或控制 Clock, 没有 Clock 则 Slave 设备不能正常⼯作。
2.2、SPI传输⽅式采⽤同步⽅式(Synchronous)传输数据Master 设备会根据将要交换的数据来产⽣相应的时钟脉冲(Clock Pulse), 时钟脉冲组成了时钟信号(Clock Signal) , 时钟信号通过时钟极性(CPOL) 和时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进⾏采样, 来保证数据在两个设备之间是同步传输的。
2.3、SPI数据交换SPI数据交换框图上图只是对 SPI 设备间通信的⼀个简单的描述, 下⾯就来解释⼀下图中所⽰的⼏个组件(Module):SSPBUF,Synchronous Serial Port Buffer, 泛指 SPI 设备⾥⾯的内部缓冲区, ⼀般在物理上是以 FIFO 的形式, 保存传输过程中的临时数据; SSPSR, Synchronous Serial Port Register, 泛指 SPI 设备⾥⾯的移位寄存器(Shift Regitser), 它的作⽤是根据设置好的数据位宽(bit-width) 把数据移⼊或者移出 SSPBUF;Controller, 泛指 SPI 设备⾥⾯的控制寄存器, 可以通过配置它们来设置 SPI 总线的传输模式。
STM32F4学习笔记之SPI(使用固件库-非中断方式)
STM32F4学习笔记之SPI(使用固件库-非中断方式)1.使能对应SPI模块时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE) for SPI1RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE) for SPI2RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, ENABLE) for SPI3.2.使能对应GPIO的时钟RCC_AHB1PeriphClockCmd() function. (SCK, MOSI, MISO and NSS ).3.配置对应引脚的复用功能GPIO_PinAFConfig(GPIOx, GPIO_PinSourcex, GPIO_AF_SPIx); //引脚映射GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 设置为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x ; //引脚选择GPIO_InitStructure.GPIO_Speed = GPIO_Speed_xxMHz; //速度选择GPIO_Init(GPIOx, &GPIO_InitStructure); //写入配置信息4.配置SPI信息SPI_InitStructure.SPI_Direction = xxx;//工作模式SPI_Direction_2Lines_FullDuplex,SPI_Direction_2Lines_RxOnly等SPI_InitStructure.SPI_Mode = xxx; //主从模式选择SPI_Mode_Master,SPI_Mode_Slave SPI_InitStructure.SPI_DataSize = xxx;//数据位选择SPI_DataSize_8b,SPI_DataSize_16b SPI_InitStructure.SPI_CPOL = xxx;//时钟空闲电平选择SPI_CPOL_High,SPI_CPOL_Low SPI_InitStructure.SPI_CPHA = xxx;//数据捕捉跳变沿选择SPI_CPHA_2Edge,SPI_CPHA_1EdgeSPI_InitStructure.SPI_NSS = xxx;//NSS信号由硬件还是软件控制SPI_NSS_Soft,#define KSZ8873_SPI_SCK_GPIO_PORT GPIOB#define KSZ8873_SPI_SCK_GPIO_CLK RCC_AHB1Periph_GPIOB#define KSZ8873_SPI_SCK_SOURCE GPIO_PinSource3#define KSZ8873_SPI_MISO_PIN GPIO_Pin_4#define KSZ8873_SPI_MISO_GPIO_PORT GPIOB#define KSZ8873_SPI_MISO_GPIO_CLK RCC_AHB1Periph_GPIOB#define KSZ8873_SPI_MISO_SOURCE GPIO_PinSource4#define KSZ8873_SPI_MOSI_PIN GPIO_Pin_5#define KSZ8873_SPI_MOSI_GPIO_PORT GPIOB#define KSZ8873_SPI_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOB#define KSZ8873_SPI_MOSI_SOURCE GPIO_PinSource5#define KSZ8873_CS_PIN GPIO_Pin_15#define KSZ8873_CS_GPIO_PORT GPIOA#define KSZ8873_CS_GPIO_CLK RCC_AHB1Periph_GPIOA#define KSZ8873_CS_LOW() GPIO_ResetBits(KSZ8873_CS_GPIO_PORT, KSZ8873_CS_PIN)#define KSZ8873_CS_HIGH() GPIO_SetBits(KSZ8873_CS_GPIO_PORT, KSZ8873_CS_PIN)#define KSZ8873_WRITE_CMD 0x02#define KSZ8873_READ_CMD 0x03程序:void KSZ8873_SPI_Init(void){SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(KSZ8873_CS_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = KSZ8873_CS_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(KSZ8873_CS_GPIO_PORT, &GPIO_InitStructure);RCC_AHB1PeriphClockCmd(KSZ8873_SPI_SCK_GPIO_CLK | KSZ8873_SPI_MISO_GPIO_CLK |KSZ8873_SPI_MOSI_GPIO_CLK, ENABLE);RCC_APB2PeriphClockCmd(KSZ8873_SPI_CLK, ENABLE);GPIO_PinAFConfig(KSZ8873_SPI_SCK_GPIO_PORT, KSZ8873_SPI_SCK_SOURCE, GPIO_AF_SPI1);GPIO_PinAFConfig(KSZ8873_SPI_MISO_GPIO_PORT, KSZ8873_SPI_MISO_SOURCE, GPIO_AF_SPI1);GPIO_PinAFConfig(KSZ8873_SPI_MOSI_GPIO_PORT, KSZ8873_SPI_MOSI_SOURCE, GPIO_AF_SPI1);GPIO_InitStructure.GPIO_Pin = KSZ8873_SPI_SCK_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(KSZ8873_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = KSZ8873_SPI_MISO_PIN;GPIO_Init(KSZ8873_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = KSZ8873_SPI_MOSI_PIN;GPIO_Init(KSZ8873_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);KSZ8873_CS_HIGH();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_4;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(KSZ8873_SPI, &SPI_InitStructure);SPI_Cmd(SPI1, ENABLE);}void KSZ8873_SPI_SendByte(u8 addr,u8 byte){KSZ8873_CS_LOW();while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(KSZ8873_SPI, KSZ8873_WRITE_CMD);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);SPI_I2S_ReceiveData(KSZ8873_SPI);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(KSZ8873_SPI, addr);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);SPI_I2S_ReceiveData(KSZ8873_SPI);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, byte);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_RXNE) == RESET);SPI_I2S_ReceiveData(KSZ8873_SPI);KSZ8873_CS_HIGH();}u8 KSZ8873_SPI_ReceiveByte(u8 byte){u8 i;KSZ8873_CS_LOW();while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, KSZ8873_READ_CMD);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_RXNE) == RESET);SPI_I2S_ReceiveData(SPI1);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, byte);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_RXNE) == RESET);SPI_I2S_ReceiveData(SPI1);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, byte);while (SPI_I2S_GetFlagStatus(KSZ8873_SPI, SPI_I2S_FLAG_RXNE) == RESET);i=SPI_I2S_ReceiveData(SPI1);KSZ8873_CS_HIGH();return i;}。
SPI1SPI2_DMA通信实验(STM32)
SPI1SPI2_DMA通信实验(STM32)STM32学习笔记(⼆)——之SPI_DMA寄存器级操作⼀、实验⽬标学会配置STM32的SPI寄存器和DMA寄存器,实现STM32的SPI1与SPI2通信功能,每次发送⼀字节数据,并可多次发送,如果接收的数据正确,则点亮LED灯。
⼆、实验⽬的加⼊DMA的SPI通信相对于普通SPI通信有什么好处?ST给SPI加了DMA功能出于什么⽬的?我觉得这是很重要的⼀个问题,⼀直边学习边想。
以下是我的看法:减少CPU负荷?我想这应该是DMA最主要的功能,可是对于SPI通信来说,其实⼤部分时候我们需要根据发送的指令->⽬标器件的应答来决定下⼀个指令,所以此时CPU还是需要⼀直等待每次通信的结束。
⽽且像SD卡的操作,是⼀个顺序流的指令操作过程,⽤中断也不容易控制。
那到底加⼊了DMA有什么好处?仔细查看了STM32F10xxx的⽤户⼿册,发现这么⼀⾏字“连续和⾮连续传输:当在主模式下发送数据时,如果软件⾜够快,能够在检测到每次TXE的上升沿(或TXE中断),并⽴即在正在进⾏的传输结束之前写⼊SPI_DR寄存器,则能够实现连续的通信;此时,在每个数据项的传输之间的SPI时钟保持连续,同时BSY位不会被清除。
如果软件不够快,则会导致不连续的通信;这时,在每个数据传输之间会被清除”以及也就是说如果连续传输⽽不使⽤DMA的话,需要CPU不停检测TXE并很快地置⼊SPI->DR的值,对于复杂程序的话这是很难达到的,⽽如果使⽤DMA,就可以轻易实现连续传输,CPU只需等待其完成就好。
我想到的⼀个应⽤就是在写SD卡的时候,每次写⼀个块512字节,就可以⽤到,能提⾼SD卡的写⼊数据速率。
其次还可以降低功耗,记得数字集成电路⽼师说过⼀句话“软件上降低数字电路功耗的⼀个⽅法就是减少电平转换。
”那么连续通信的时候,像SPI的BSY电平转换会⼤⼤减少!最后⼀点,虽然效果不⼤,就是如果不是⽤DMA,那么CPU的⼯作就是搬运⼯,把SPI->DR 的内容搬到内存存储起来,⽽如果使⽤DMA,就省略了这个环节!我想,为什么实现同⼀个功能,有的执⾏起来很流畅,有的却很卡,应该和这些⼩细节的减载有关吧。
stm32学习笔记--spi与iic
stm32学习笔记--spi与iic关于上次说的要改程序的问题,//读ADXL345 寄存器//addr:寄存器地址//返回值:读到的值u8 ADXL345_RD_Reg(u8 addr){u8 temp=0; IIC_Start(); IIC_Send_Byte(ADXL_WRITE); //发送写器件指令temp=IIC_Wait_Ack(); IIC_Send_Byte(addr); //发送寄存器地址temp=IIC_Wait_Ack(); IIC_Start(); //重新启动IIC_Send_Byte(ADXL_READ); //发送读器件指令temp=IIC_Wait_Ack(); temp=IIC_Read_Byte(0); //读取一个字节,不继续再读,发送NAK IIC_Stop(); //产生一个停止条件return temp; //返回读到的值} 这段写寄存器代码,不理解temp 为什么要被频繁的赋值,去掉后,宏观看来对结果没有影响。
第二个不理解的地方是为什么在发送寄存器地址之后要从新启动一次,因为在相似的写寄存器函数中,在相同的位置不存在重启代码。
注释掉该句之后显示ADXL345 error。
这两天主要看了三轴加速度计的程序,虽然例程里的能看懂,但是在四轴里的程序却不那么容易,我甚至不明白为什么他要自己写一个iic 的函数,我打算接下来把它的程序和例程里的程序对照来看,看能不能找到什么头绪。
下面是对以前学过内容的总结:对位的寻址操作为了实现对SARM、I/O 外设空间中某一位的操作,在寻址空间(4GB)另一地方取个别名区空间,从这地址开始,每一个字(32bit)就对应SRAM 或I/O 的一位。
即原来每个字节用一个地址,现在给字节中的每个位一个地址,实现了对位的寻址。
spi 与iic 之间各自的优劣1 硬件连接的优劣SPI 是[单主设备(single-master )]通信协议,这意味着总线中的只有一支中心设备能发起通信。
STM32
STM32 SPI通信--OLED⼀、0.96⼨OLED⼆、原理图⼆、GPIO模拟SPI1. 硬件连接通过引脚和模块电路图可以分析出SPI的电路连接OLED STM32GND <----------> GNDVCC <----------> 3.3VD0 <----------> PA4(CLK)D1 <----------> PA3(MOSI)RES <----------> PA2(RET复位)DC <----------> PA1(命令|数据dc)CS <----------> GND2. 软件驱动由OLED模块数据⼿册上SPI的操作时序图写出由GPIO⼝模拟的SPI驱动代码,模块只⽀持向模块写数据不能读数据,所以只需要写SPI发送即可2.1 “SPI.h”#define OLED_CMD 0 //命令声明#define OLED_DATA 1 //数据声明#define OLED_CLK PAout(4) // CLK时钟 d0#define OLED_MOSI PAout(3) // MOSI d1#define OLED_RST PAout(2) // RET复位 ret#define OLED_DC PAout(1) // 命令|数据 dc (0表传输命令1表传输数据)void OLED_SPI_Init(void); //配置MCU的SPIvoid SPI_WriteByte(uint8_t addr,uint8_t data); //向寄存器地址写⼀个byte的数据void WriteCmd(unsigned char cmd); //写命令void WriteDat(unsigned char data); //写数据2.2 “SPI.c”void OLED_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能PA端⼝时钟GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //端⼝配置GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO⼝速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOA}void SPI_WriteByte(unsigned char data,unsigned char cmd){unsigned char i=0;OLED_DC =cmd;OLED_CLK=0;for(i=0;i<8;i++){OLED_CLK=0;if(data&0x80)OLED_MOSI=1; //从⾼位到低位else OLED_MOSI=0;OLED_CLK=1;data<<=1;}OLED_CLK=1;OLED_DC=1;}void WriteCmd(unsigned char cmd){SPI_WriteByte(cmd,OLED_CMD);}void WriteData(unsigned char data){SPI_WriteByte(data,OLED_DATA);}三、STM32对OLED模块的控制3.1 指令集3.2 “OLED.h”void OLED_Init(void);//初始化OLEDvoid OLED_ON(void);//唤醒OLEDvoid OLED_OFF(void);//OLED休眠void OLED_Refresh_Gram(void);//更新显存到OLEDvoid OLED_Clear(void);//清屏void OLED_DrawPoint(u8 x,u8 y,u8 t);//画点void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);//填充void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);//显⽰字符void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);//显⽰2个数字void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size);//显⽰字符串3.3 “OLED.c”//OLED的显存//存放格式如下.//[0]0 1 2 3 (127)//[1]0 1 2 3 (127)//[2]0 1 2 3 (127)//[3]0 1 2 3 (127)//[4]0 1 2 3 (127)//[5]0 1 2 3 (127)//[6]0 1 2 3 (127)//[7]0 1 2 3 (127)u8 OLED_GRAM[128][8];void OLED_DLY_ms(unsigned int ms){unsigned int a;while(ms){a=1335;while(a--);ms--;}}void OLED_Init(void){OLED_SPI_Init();OLED_CLK = 1;OLED_RST = 0;OLED_DLY_ms(100);OLED_RST = 1;//从上电到下⾯开始初始化要有⾜够的时间,即等待RC复位完毕WriteCmd(0xAE); // Display Off (0x00)WriteCmd(0xD5);WriteCmd(0x80); // Set Clock as 100 Frames/SecWriteCmd(0xA8);WriteCmd(0x3F); // 1/64 Duty (0x0F~0x3F)WriteCmd(0xD3);WriteCmd(0x00); // Shift Mapping RAM Counter (0x00~0x3F)WriteCmd(0x40 | 0x00); // Set Mapping RAM Display Start Line (0x00~0x3F) WriteCmd(0x8D);WriteCmd(0x10 | 0x04); // Enable Embedded DC/DC Converter (0x00/0x04) WriteCmd(0x20);WriteCmd(0x02); // Set Page Addressing Mode (0x00/0x01/0x02)WriteCmd(0xA0 | 0x01); // Set SEG/Column MappingWriteCmd(0xC0); // Set COM/x Scan DirectionWriteCmd(0xDA);WriteCmd(0x02 | 0x10); // Set Sequential Configuration (0x00/0x10)WriteCmd(0x81);WriteCmd(0xCF); // Set SEG Output CurrentWriteCmd(0xD9);WriteCmd(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock WriteCmd(0xDB);WriteCmd(0x40); // Set VCOM Deselect LevelWriteCmd(0xA4 | 0x00); // Disable Entire Display On (0x00/0x01)WriteCmd(0xA6 | 0x00); // Disable Inverse Display On (0x00/0x01)WriteCmd(0xAE | 0x01); // Display On (0x01)OLED_Clear(); //初始清屏}void OLED_ON(void){WriteCmd(0X8D); //设置电荷泵WriteCmd(0X14); //开启电荷泵WriteCmd(0XAF); //OLED唤醒}void OLED_OFF(void){WriteCmd(0X8D); //设置电荷泵WriteCmd(0X10); //关闭电荷泵WriteCmd(0XAE); //OLED休眠}void OLED_Refresh_Gram(void){u8 i,n;for(i=0;i<8;i++){WriteCmd(0xb0+i); //设置页地址(0~7)WriteCmd(0x00); //设置显⽰位置—列低地址WriteCmd(0x10); //设置显⽰位置—列⾼地址for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]);}}void OLED_Clear(void){u8 i,n;for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;OLED_Refresh_Gram();//更新显⽰}void OLED_DrawPoint(u8 x,u8 y,u8 t){u8 pos,bx,temp=0;if(x>127||y>63)return;//超出范围了.pos=7-y/8;bx=y%8;temp=1<<(7-bx);if(t)OLED_GRAM[x][pos]|=temp;else OLED_GRAM[x][pos]&=~temp;}void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot){u8 x,y;for(x=x1;x<=x2;x++){for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);}OLED_Refresh_Gram();//更新显⽰}void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode){u8 temp,t,t1;u8 y0=y;u8 csize=(size/8+((size%8)?1:0))*(size/2); //得到字体⼀个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值for(t=0;t{if(size==12)temp=asc2_1206[chr][t]; //调⽤1206字体else if(size==16)temp=asc2_1608[chr][t]; //调⽤1608字体else if(size==24)temp=asc2_2412[chr][t]; //调⽤2412字体else return; //没有的字库for(t1=0;t1<8;t1++){if(temp&0x80)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp<<=1;y++;if((y-y0)==size){y=y0;x++;break;}}}}//m^n函数u32 mypow(u8 m,u8 n){u32 result=1;while(n--)result*=m;return result;}void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size){u8 t,temp;u8 enshow=0;for(t=0;t{temp=(num/mypow(10,len-t-1));if(enshow==0&&t<(len-1)){if(temp==0){OLED_ShowChar(x+(size/2)*t,y,' ',size,1);continue;}else enshow=1;}OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1);}}void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size){while((*p<='~')&&(*p>=' '))//判断是不是⾮法字符!{if(x>(128-(size/2))){x=0;y+=size;}if(y>(64-size)){y=x=0;OLED_Clear();}OLED_ShowChar(x,y,*p,size,1);x+=size/2;p++;}四、⽤STM32的SPI资源也可以不使⽤GPIO⼝模拟,使⽤STM32的SPI资源,驱动代码如下:void OLED_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE ); //①GPIO,SPI 时钟使能GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复⽤推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7); //①初始化 GPIOGPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;//端⼝配置GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO⼝速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOASPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置 SPI 全双⼯ SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置 SPI ⼯作模式:设置为主 SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8 位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串⾏时钟的稳态:时钟悬空⾼ SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第⼆个时钟沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由硬件管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //预分频 256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式SPI_Init(SPI1, &SPI_InitStructure); //②根据指定的参数初始化外设 SPIx 寄存器SPI_Cmd(SPI1, ENABLE); //③使能 SPI 外设//SPI1_ReadWriteByte(0xff);//④启动传输}void OLED_WB(uint8_t data){while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, data);}void SPI_WriteByte(unsigned char data,unsigned char cmd) {unsigned char i=0;OLED_DC =cmd;OLED_WB(data);}。
STM32(三十八)RFID介绍-使用SPI对RFID标签进行读写
STM32(三⼗⼋)RFID介绍-使⽤SPI对RFID标签进⾏读写⼀、概述射频识别(RFID)是 Radio Frequency Identification 的缩写。
其原理为阅读器与标签之间进⾏⾮接触式的数据通信,达到识别⽬标的⽬的。
RFID 的应⽤⾮常⼴泛,典型应⽤有动物晶⽚、汽车晶⽚防盗器、门禁管制、停车场管制、⽣产线⾃动化、物料管理.特点:⾃动识别技术的⼀种。
通过⽆线射频⽅式进⾏⾮接触双向数据通信利⽤⽆线射频⽅式对记录媒体(电⼦标签或射频卡)进⾏读写,从⽽达到识别⽬标和数据交换的⽬的。
典型的RFID系统主要包括两部分:射频卡/标签(Tag)和读写器( Reader) 。
其系统结构和基本⼯作原理如图1所⽰。
当前RFID技术研究主要集中在⼯作频率选择、天线设计、防冲突技术和安全与隐私保护等⽅⾯。
标签适⽤于对象⾝份识别,对象可以是⼈或物体。
标签的主要模块集成在⼀个芯⽚中,完成与读写器通信的功能;芯⽚上有内存⽤来存储ID或其他数据,其容量从⼏个⽐特到⼏千个⽐特;芯⽚外围连接天线或电池。
RFID标签依据发送射频信号的⽅式不同,分为主动式(Active)和被动式( Passive)两种,主动式标签特点:能主动向读写器发送射频信号,通常由内置电池供电,⼜称为有源标签,通信距离远,其价格相对较⾼,主要应⽤于贵重物品远距离检测等应⽤领域。
被动式标签特点:被动式标签不带电池,⼜称为⽆源标签,从读写器的询问信号中获取能量⼯作,具有价格便宜的优势。
⼯作距离短、存储容量有限,主要⽤于近距离识别系统。
读写器主要由⼀个RF模块和控制单元组成,通常有内置天线,通过射频信号与标签通信。
读写器可以通过有线连接或⽆线连接与计算机系统相连,把接收到的标签信息送到主机进⾏相应处理。
⼆、RFID模块介绍1、 芯⽚特点:⾼度集成的⾮接触式(13.56MHz)读写卡芯⽚,此发送模块利⽤调制和调节的原理,并将它们完全集成到各种⾮接触式通信⽅法和协议中。
第2章 STM32单片机结构和最小系统
STM32F10x 处理器总线结构
总线结构中各单元的功能 ICode 总线:将 Flash 存储器指令接口与 Cortex-M3 内核的指令总线相连接,用于指 令预取; DCode 总线:将 Flash 存储器的数据接口与 Cortex-M3 内核的 DCode 总线相连接,用于 常量加载和调试访问; System 总线:将Cortex-M3 内核的 System 总线(外设总线)连接到总线矩阵;
退出
2.2 STM32F103总线和存储器结构
ICode Flash接口 DCode Cortex-M3 System SRAM DMA1 通道1 通道2 Flash接口
总线矩阵
DMA
复位和时钟 控制(RCC)
桥接1
DMA
…
AHB
桥接2
APB2
APB1 DAC SPI3/I2S PWR SPI2/I2S IWDG BKP CAN1 WWDG RTC CAN2 TIM7 I2C2 TIM6 I2C1 UART5 TIM5 UART4 TIM4 USART3 TIM3 USART2 TIM2
嵌入式单片机原理及应用
退出
1 2 3 4 5 6 7 8 9 10 11
ARM嵌入式系统概述 STM32单片机结构和最小系统 基于标准外设库的C语言程序设计基础 STM32通用输入输出GPIO STM32外部中断 STM32通用定时器 STM32通用同步/异步收发器USART 直接存储器存取DMA STM32的模数转换器ADC STM32的集成电路总线I2C STM32的串行外设接口SPI
总线结构中各单元的功能 DMA 总线:将DMA 的 AHB 主控接口与总线 矩阵相连; 总线矩阵:用于连接三个主动单元部件和三个 被动单元,负责协调和仲裁Cortex-M3 内核和 DMA 对 SRAM 的访问,仲裁采用轮换算法。 AHB/APB 桥:两个 AHB/APB 桥在 AHB 和 2 个 APB 总线之间提供完全同步连接。
STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从, CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
单片机STM32学习笔记
推挽输出与开漏输出的区别推挽输出推挽输出::可以输出高可以输出高,,低电平低电平,,连接数字器件连接数字器件; ;开漏输出开漏输出::输出端相当于三极管的集电极输出端相当于三极管的集电极. . 要得到高电平状态需要上拉电阻才行要得到高电平状态需要上拉电阻才行. . 适合于做电流型的驱动电流型的驱动,,其吸收电流的能力相对强其吸收电流的能力相对强((一般20ma 以内以内). ).推挽结构一般是指两个三极管分别受两互补信号的控制推挽结构一般是指两个三极管分别受两互补信号的控制,,总是在一个三极管导通的时候另一个截止另一个截止. .要实现“线与”需要用OC(open collector)collector)门电路门电路门电路..是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中以推挽方式存在于电路中,,各负责正负半周的波形放大任务各负责正负半周的波形放大任务,,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小关管每次只有一个导通,所以导通损耗小,,效率高。
输出既可以向负载灌电流,也可以从负载抽取电流。
抽取电流。
问题:问题:很多芯片的供电电压不一样,有3.3v 和5.0v 5.0v,需要把几种,需要把几种IC 的不同口连接在一起,是不是直接连接就可以了?实际上系统是应用在I2C 上面。
上面。
简答:简答:1、部分3.3V 器件有5V 兼容性,可以利用这种容性直接连接兼容性,可以利用这种容性直接连接2、应用电压转换器件,如TPS76733就是5V 输入,转换成3.3V 3.3V、、1A 输出。
输出。
开漏电路特点及应用在电路设计时我们常常遇到开漏(在电路设计时我们常常遇到开漏(open drain open drain )和开集()和开集()和开集(open collector open collector )的概念。
所)的概念。
所谓开漏电路概念中提到的“漏”就是指MOSFET 的漏极。
STM32-SPI
4月25日---- 4月27日SPI总线经过两天的学习,哈哈SPI终于搞定,用STM32控制IIC EEPORM,将EEPROM中的数据读到SPI的FLASH中。
---------------------------------------------------------------------------------------------------------------------- SPI(Serial Peripheral Interface)总线:串行外围总线接口一、SPI总线的简介:SPI是由摩托罗拉公司开发的全双工同步串行总线,没有总线仲裁机制,所以工作在单主机系统中。
主设备启动与从设备的同步通讯,从而完成数据的交换。
对于只能做从机的器件接口来说,SPI接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟)和CS(从使能信号)。
通讯时,数据由SDO输出,SDI输入,数据在时钟的上升沿或者下降沿由SDO输出,在紧接着的下降沿或者上升沿由SDI读入,这样经过8/16帧,以完成8/16位数据的传输。
对于有的微控制器来说,它的硬件SPI即可做主机,也可以做从机,即可实现主从配置,MOSI:主出从入、MISO:主入从出、SCK:串行时钟、SS:从属选择。
(当做主机输出时,该信号用于选中需要访问的从机,SS输入高电平,外部决定其为主机;当做从机时,SS为输入或者一般的IO口使用)。
常用的SPI接法:在软件配置的条件下SS不使用。
1、SPI总线的通信时序(1)在SPI通信中,在全双工模式下,发送和接收是同事进行的(2)数据传输的时钟基来自主控制器的时钟脉冲;摩托罗拉没有定义任何通用的SPI 时钟的规范,最常用的时钟设置是基于时钟极性CPOL和时钟相位CPHA两个参数。
a)CPOL=0,表示时钟的空闲状态为低电平b)CPOL=1,表示时钟的空闲状态为高电平c)CPHA=0,表示同步始终的第一个边沿(上升或者下降)数据被采样d)CPHA=1,表示同步始终的第二个边沿(上升或者下降)数据被采样即CPOL和CPHA的设置决定了数据采样的时钟沿。
STM32SPI接口的简单实现要点
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位组合。
STM32学习重点spi剖析
一、SPI简介SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。
是Motorola首先在其MC68HCXX系列处理器上定义的。
SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32也有SPI接口。
SPI接口一般使用4条线:MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK时钟信号,由主设备产生。
CS从设备片选信号,由主设备控制。
SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
SPI总线四种工作方式SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。
如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。
时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。
如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。
SPI主模块和与之通信的外设备时钟相位和极性应该一致。
不同时钟相位下的总线数据传输时序见下图:二、STM32的SPI介绍STM32的SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议。
本节,我们将利用STM32的SPI来读取外部SPI FLASH芯片(W25X16),这节,我们使用STM32的SPI1的主模式,STM32的主模式配置步骤如下:1)配置相关引脚的复用功能,使能SPI1时钟。
STM32 FLASH实验-SPI
所以代码为:
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//
选择全双工 SPI 模式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8 位 SPI
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
//时钟悬空高
电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
//在第二个时
钟采集数据
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
//Nss 使用软件控
2、配置 GPIO 口的模式。 这这里我们要打开 GPIOG 和 GPIOB 的时钟,然后将片选端 PG13 设置
为推挽输出,然后设置 SPI 使用的 GPIO 口,将 PB13、PB14 和 PB15 设置 为复用推挽输入。大家可能疑惑为什么 PB14 也设置为输出呢?它不是应该 输入吗?在这里它是作为复用端口了,所以设置为复用推挽输出就是跟外设 连接,要外设自己来决定输入输出了。
/* SPI 的 IO 口设置 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);
第1章 STM32基本原理
0x0000 0014 AHB设备时钟使能寄存器
0x0000 0000 APB2设备时钟使能寄存器 0x0000 0000 APB1设备时钟使能寄存器
1.7输入输出端口GPIO 1.7.1常规输入输出GPIO
• GPIO包括多个16位I/O端口,每个端口可以独立设置3工作 模式,并可独立地置位或复位 • GPIO由寄存器、输入驱动器和输出驱动器等部分组成 • GPIO是可编程输入/输出端口
(8)可变静态存储控制器FSMC。
(9)DMA通道。
1.1 STM32性能和结构 1.1.3 芯片封装和管脚概述
1.2
Cortex M3处理器
Cortex-M3简介
• Cortex-M3是采用哈佛结构的32位处理器内核,拥有独 立的指令总线和数据总线,两者共享同一个4GB存储空 间 • Cortex-M3内建一个嵌套向量中断控制器(NVIC: Nested Vectored Interrupt Controller),支持可嵌套 中断、向量中断和动态优先级等 • Cortex-M3内部还包含一个系统滴答定时器SysTick
1.6复位和时钟控制RCC
STM32 MCU系统时钟树 • 系统时钟树中的时钟选择、预分频值和外设时钟使能等 都可以通过对复位和时钟控制(RCC)寄存器编程实 现
偏移 地址 名称 类型 复位值 说 明
0x14 RCC_AHBENR
0x18 RCC_APB2ENR 0x1C RCC_APB1ENR
读/写
1.7输入输出端口GPIO
1.7.1常规输入输出GPIO
1.7输入输出端口GPIO 1.7.1常规输入输出GPIO
• GPIO通过7个32位寄存器进行操作
偏移地址 0x00 0x04 0x08 0x0C 0x10 0x14 0x18 名称 CRL CRH IDR ODR BSRR BRR LCKR 类型 复位值 说 明
学习6__STM32--SPI外设之中断收发---
学习6__STM32--SPI外设之中断收发---<⽬标> STM32双机 SPI中断收发通信<描述> # STM32双机配置为⼀主⼀从模式 # 采⽤主机中断发送,从机中断接收 # 收发机制采⽤不间断收发(发送为空就发送,接收⾮空就接收,中间⽆其他操作打断) # 就是单字节发送与接收<问题> 从机接收端会出现,接收到的数据可能是原始发送数据也会是错误数据,出现这种现象的条件是发送主机复位、发送主机重新上电、随时间变化(物理碰触等)都会产⽣错误数据,⽽复位接收从机、重新上电接收从机会纠正数据<分析> # STM32双机未共地导致 共地后问题依旧 # STM32未使⽤NSS引脚导致 使⽤后问题依旧 # ⼯作模式改变尝试(发送与接收⼯作模式配置为不匹配) 问题依旧 # 主机发送太过频繁导致,导致接收来不及接收导致 拉⼤发送数据周期问题依旧 # 从数据结果上分析,应该是发送主机与接收从机未同步导致,接收总线的数据先由移位寄存器接收,再copy⾄数据寄存器,所以分析数据错位现象是出现在移位寄存器中,⽐如正在传输中由复位操作或断电操作等,致使移位寄存器只接收了3bit数据,⽽SPI数据的接收机制是,移位寄存器收满8bit数据后copy⾄数据寄存器,这⼀切都是硬件完成,注意数据的搬移是copy,所以移位寄存器中的数据还在即数据残留特性,就像刚刚的这种中断操作⾏为导致移位寄存器残留了当前字节的3bit数据,未满8bit数据故不会copy⾄数据寄存器,所以等待恢复⼯作后,需要再接收5bit数据,这样满8bit数据后copy⾄spi->DR,但是这1byte数据中的前3bit与后5bit数据本不是⼀个有效byte数据,就导致读到1byte⽆效数据,产⽣了接收错误数据的现象<解决> # 拉⼤发送数据周期&在进⼊接收中断后先关闭SPI外设,然后再读取数据,出中断前开始SPI外设 > 在进⼊中断服务程序后关闭spi外设,将导致在关闭外设期间发⽣的中断⽽被忽视,尤其是多数据连续发送,⽐如DMA数据发送,实测将导致丢数据,即其中的部分中断未作出响应⽽丢弃。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
SPI总线与IIC类似,SPI也是一种通信协议。
今天我们就以WX25X16芯片为例来介绍SPI.首先我们来看下硬件连接。
、从原理图可以看到该芯片需要单片机控制的管脚有4个,非别是CS,DO,DIO,CLK.其中CS 是片选信号,只有将该位拉低才能选中该芯片。
DO,DIO分别是输出和输入。
CLK是时钟信号。
SPI通信的步骤如下所示:1)获取地址12)获取地址23)擦除扇区4)写入数据好的,下面我们对每个步骤进行分析(1)在对芯片操作前先要对端口及SPI外设进行相应的设置:/*函数名:SPI_FLASH_Init(void)功能:对端口和SPI初始化输入:无输出:无调用:被主函数调用*/void SPI_FLASH_Init(void){SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI1 and GPIO clocks */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);/*!< SPI_FLASH_SPI Periph clock enable */RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);/*将PA5(CLK)配置成复用推挽输出*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);/*将PA6(DO)设置成浮空输入*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOA, &GPIO_InitStructure);/将PA7(DIO)设为浮空输入/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_Init(GPIOA, &GPIO_InitStructure);/将PA4(CS)设为推挽输出/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);/拉高CS,失能芯片,该语句是宏定义,就是置高PA4/SPI_FLASH_CS_HIGH();/* SPI配置/// W25X16: data input on the DIO pin is sampled on the rising edge of the CLK.// Data on the DO and DIO pins are clocked out on the falling edge of CLK./*将SPI设为全双工模式*/SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;/*将SPI设为主模式*/SPI_InitStructure.SPI_Mode = SPI_Mode_Master;/*将SPI通信的数据大小设为8位*/SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;/*将CLK的高电平设为空闲*/SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;/*设置在第二个时钟沿捕获数据*/SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;/*指定NSS信号由软件管理*/SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;/SPI_BaudRatePrescaler用来定义波特率预分频的值,这个值用以设置发送和接收的SCK时钟/SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;/SPI_FirstBit指定了数据传输从高位还是低位开始/SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;/SPI_CRCPolynomial定义了用于CRC值计算的多项式/SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);/* Enable SPI1 */SPI_Cmd(SPI1, ENABLE);}(2)获取器件地址1因为SPI总线上可以挂很多的器件,所以首先要获得器件的地址。
获得器件地址的步骤是:拉低CS——>向器件发送获得地址的命令——>连续发送三个任意数据——>在发送第四个任意数据时在DO口上读出器件地址。
注意,发送完读地址后,从机会将数据自动传给主机的SPI 数据寄存器中,用户只要到该寄存器中取数就可以了。
好的,既然用到写命令那么我们写来写这两个最基础的程序。
写数据函数:/************************************************************* ******************* Function Name : SPI_FLASH_SendByte* Description : 通过SPI总线发送一字节数据,再通过SPI总线返回一字节数据* Input : byte ——要发送的数据* Output : 从机返回的数据* 调用:被SPI_FLASH_ReadDeviceID调用************************************************************** *****************/u8 SPI_FLASH_SendByte(u8 byte){/* 等待SPI发送寄存器里的数据发送结束*/while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);/* 发送数据*/SPI_I2S_SendData(SPI1, byte);/* 等待接收完一字节数据*/while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);/* 返回接收到的数据*/return SPI_I2S_ReceiveData(SPI1);}现在我们再来看看读器件地址函数:/************************************************************* ******************* Function Name : SPI_FLASH_ReadDeviceID(void)* Description : 读器件地址1* Input : 无* Output : 无* 返回: 器件地址调用:SPI_FLASH_SendByte************************************************************** *****************/u32 SPI_FLASH_ReadDeviceID(void){u32 Temp = 0;/* SPI_FLASH_CS_LOW(),是一个宏定义,就是拉低CS(PA4)*/ SPI_FLASH_CS_LOW();/*W25X_DeviceID=0XAB,是读器件地址的命令,发送完该命令后连续发三个任意数字,在发第五个任意数后可以读出地址*/SPI_FLASH_SendByte(0XAB);SPI_FLASH_SendByte(0XFF);SPI_FLASH_SendByte(0XFF);SPI_FLASH_SendByte(0XFF);/* Read a byte from the FLASH */Temp = SPI_FLASH_SendByte(0XFF);/* 失能芯片*/SPI_FLASH_CS_HIGH();/*返回器件地址*/return Temp;}像温度传感器一样,WX25X16也有很多的命令,具体的命令看下图:图5-1(3)获取器件地址2获取完地址1后,我们再来获取地址2.读地址2的命令是0X9F,从图5-1中可以看到,发送完0X9F后连续读出3个数据。
好的,我们来看看具体的程序:/******************************************************************************** Function Name : SPI_FLASH_ReadID* Description : 读器件地址2* Input : 无* Output : 无* 返回: 器件地址2调用:SPI_FLASH_SendByte********************************************************************* **********/u32 SPI_FLASH_ReadID(void){u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;/* 拉低CS来使能从机*/SPI_FLASH_CS_LOW();/*W25X_JedecDeviceID=0X9F,读取从机地址的命令,发送完命令后可以连续读三个字节的地址*/SPI_FLASH_SendByte(0X9F);/* Read a byte from the FLASH */Temp0 = SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp1 = SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp2 = SPI_FLASH_SendByte(Dummy_Byte);/* 拉高CS,失能从机*/SPI_FLASH_CS_HIGH();/*将三个地址合并*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp;}(4)擦除扇区WX25X16是2M的字节,共分为8页,每页是256个字节的大小。