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)读写卡芯⽚,此发送模块利⽤调制和调节的原理,并将它们完全集成到各种⾮接触式通信⽅法和协议中。
- 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个字节的大小。