门禁系统原理图设计
任务导入:什么是门禁系统
门禁系统,又称为出入管理控制系统,是一种管理人员进出的数字化管理系统。本项目主要介绍的就是非接触式射频卡门禁系统的设计,简称门禁系统设计。
系统组成(五大部分 )
组成关系
四门禁控制器
门禁控制器是门禁系统的核心,它由一台微处理机和相应的外围电路组成。
阅读器
阅读器是读取射频卡门禁卡片中的数据信息并将信息传送给控制器的设备。
电控锁
电控锁是门禁系统中执行锁门的部件。
门禁卡
门禁卡是开门的钥匙。
设计目标
门禁系统设计目标:通过 RFID技术,验证电子钥匙(射频门禁卡)的合法性,控制电子门锁的开启;对射频门禁卡信息进行管理;对用户信息进行管理;记录出入信息作为考勤管理;实现自动、安全的射频卡门禁系统的管理。
射频卡门禁系统的设计目的是实现人员出入控制、考勤数据采集、数据统计和信息查询过程的自动化;方便工作人员进出开锁与报到,方便管理人员统计、考核人员出勤情况等。
功能需求
- 刷卡开门
- 按钮开门
- 时间段设置
- 定时设置
- 远程控制
- 记录存储功能
SPI原理与相关库函数
原理
什么是SPI
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
SPI主从模式 SPI分为主、从两种模式,一个SPI通讯系统需要包含一个(且只能是一个)主设备,一个或多个从设备。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起。当存在多个从设备时,通过各自的片选信号进行管理。
SPI是全双工且SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps
SPI信号线
SPI接口一般使用四条信号线通信: SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)
MISO: 主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。 MOSI: 主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。 SCLK:串行时钟信号,由主设备产生。 CS/SS:从设备片选信号,由主设备控制。它的功能是用来作为“片选引脚”,也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
硬件上为4根线。
SPI设备选择
SPI是[单主设备( single-master )]通信协议,这意味着总线中的只有一支中心设备能发起通信。当SPI主设备想读/写[从设备]时,它首先拉低[从设备]对应的SS线(SS是低电平有效),接着开始发送工作脉冲到时钟线上,在相应的脉冲时间上,[主设备]把信号发到MOSI实现“写”,同时可对MISO采样而实现“读”,如下图:
SPI数据发送接收
SPI主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
- 首先拉低对应SS信号线,表示与该设备进行通信
- 主机通过发送SCLK时钟信号,来告诉从机写数据或者读数据 这里要注意,SCLK时钟信号可能是低电平有效,也可能是高电平有效,因为SPI有四种模式,这个我们在下面会介绍
- 主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(0~7),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。
- 从机(Slave)也将自己的串行移位寄存器(0~7)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。
SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。
SPI通信的四种模式
SPI通信有4种不同的操作模式,不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来 控制我们主设备的通信模式,具体如下:
-
时钟极性(CPOL)定义了时钟空闲状态电平:
-
- CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
-
- CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时
-
时钟相位(CPHA)定义数据的采集时间。
-
- CPHA=0,在时钟的第一个跳变沿(上升沿或下降沿)进行数据采样。,在第2个边沿发送数据
-
- CPHA=1,在时钟的第二个跳变沿(上升沿或下降沿)进行数据采样。,在第1个边沿发送数据
例如:
-
Mode0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。
-
Mode1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
-
Mode2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
-
Mode3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
它们的区别是定义了在时钟脉冲的哪条边沿转换(toggles)输出信号,哪条边沿采样输入信号,还有时钟脉冲的稳定电平值(就是时钟信号无效时是高还是低)。每种模式由一对参数刻画,它们称为时钟极(clock polarity)CPOL与时钟期(clock phase)CPHA。
SPI通信协议
主从设备必须使用相同的工作模式——SCLK、CPOL 和 CPHA,才能正常工作。如果有多个从设备,并且它们使用了不同的工作模式,那么主设备必须在读写不同从设备时需要重新修改对应从设备的模式。以上SPI总线协议的主要内容。
是不是感觉,这就完了? SPI就是如此,他没有规定最大传输速率,没有地址方案,也没规定通信应答机制,没有规定流控制规则。
只要四根信号线连接正确,SPI模式相同,将CS/SS信号线拉低,即可以直接通信,一次一个字节的传输,读写数据同时操作,这就是SPI
些通信控制都得通过SPI设备自行实现,SPI并不关心物理接口的电气特性,例如信号的标准电压。
PS: 这也是SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
SPI的三种模式
-
运行模式(Run Mode) 这是基本的操作模式
-
等待模式(Wait Mode) SPI工作在等待模式是一种可配置的低功耗模式,可以通过SPICR2寄存器的SPISWAI位进行控制。在等待模式下,如果SPISWAI位清0,SPI操作类似于运行模式。如果SPISWAI位置1,SPI进入低功耗状态,并且SPI时钟将关闭。如果SPI配置为主机,所有的传输将停止,但是会在CPU进入运行模式后重新开始。如果SPI配置为从机,会继续接收和传输一个字节,这样就保证从机与主机同步。
-
停止模式(Stop Mode) 为了降低功耗,SPI在停止模式是不活跃的。如果SPI配置为主机,正在进行的传输会停止,但是在CPU进入运行模式后会重新开始。如果SPI配置为从机,会继续接受和发送一个字节,这样就保证了从机与主机同步。
相关库函数
1.初始化GPIO口,配置相关引脚的复用功能,使能SPIx时钟。调用函数:void GPIO_Init();
2.使能SPI时钟总线:RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE)
3.配置SPI初始化的参数,设置SPI工作模式:SPI_Init(SPI1,&SPI_Initstructure)
4.使能SPI外设:SPI_Cmd(SPI1,ENABLE);
SPI 配置设置
typedef struct
{
uint16_t SPI_Direction; /*!< 传输方向,两向全双工,单向接收等*/
uint16_t SPI_Mode; /*!< 模式选择,确定主机还是从机 */
uint16_t SPI_DataSize; /*!< 数据大小,8位还是16位 */
uint16_t SPI_CPOL; /*!< 时钟极性选择 */
uint16_t SPI_CPHA; /*!< 时钟相位选择 */
uint16_t SPI_NSS; /*!< 片选是硬件还是软件*/
uint16_t SPI_BaudRatePrescaler; /*!< 分频系数 */
uint16_t SPI_FirstBit; /*!< 指定数据传输是从MSB还是LSB位开始的。M
SB就是二进制第一位,LSB就是最后一位 */
uint16_t SPI_CRCPolynomial; /*!< CRC校验 ,设置 CRC 校验多项式,提高通
信可靠性,大于 1 即可*/
}SPI_InitTypeDef;
参数解读
void SPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉
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(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
SPI2_ReadWriteByte(0xff);//启动传输
}
u8 SPIx_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
{
}//等待发送区空
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
{
} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}
uint8_t SPI_SendByte(uint8_t byte)
{
uint8_t d_read,d_send=byte;
if(HAL_SPI_TransmitReceive(&hspi1,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)
d_read=0XFF;
return d_read;
}
FSMC原理与相关库函数
FSMC( Flexible static memory controller)全称“灵活的静态存储器控制器”,是 STM32中一个很有特色的外设,通过 FSMC,STM32可以通过FSMC与SRAM、ROM、PSRAM、Nor Flash和NandFlash存储器的引脚相连,从而进行数据的交换。
要注意的是,FSMC 只能扩展静态的内存,即static(静态的),不能是动态的内存,比如 SDRAM 就不能扩展。
关于DRAM跟SRAM等内存存储,如果不懂请看这篇:
SDRAM、DRAM及DDR FLASH ROM概念详解
FSMC通俗讲解
单片机跟外部存储器通信,需要知道数据的地址,然后把数据的内容传递过来。如此一来,存储器就需要地址传输线跟数据传输线,还要加上一些控制时序引脚 比如复位、写数据、读数据等。
1 地址线:是用来传输地址信息用的。举个简单的例子:cpu在内存或硬盘里面寻找一个数据时,先通过地址线找到地址,然后再通过数据线将数据取出来。 如果有32根.就可以访问2的32次方的字节,也就是4GB。
2 数据线(data cable),用来传递数据或通信。通俗点说,就是单片机发送指令给存储器,和存储器发送数据给单片机这两个功能。
DRAM存储器的原理图
图中A0 ~ 18为地址线,总共19根地址线(即2^19=512K,1K=1024); DQ0 ~ 15为数据线,总共16根数据线。 CEn是芯片使能信号,低电平有效; OEn是输出使能信号,低电平有效; WEn是写使能信号,低电平有效; BLEn和BHEn分别是低字节控制和高字节控制信号;
假设不用FSMC,直接跟DRAM通信,正常通过IO口的时序逻辑跟DRAM通信是非常繁琐的。
STM32自带的FSMC功能,就是专门为这类存储器设计的,在STM32上,有一些引脚被专门设计成地址线,还有一些被专门设计成数据线,还有一些被设计成控制线,然后这些地址线和数据线对应着固定的地址,只要外部的DRAM等存储器将对应的数据线连接到STM32这些对应的引脚上,引脚功能设置为复用模式,通过配置FSMC ,就可以直接给那个固定的地址赋值 ,其他操作STM32都会自动给你完成,就可以把数据存储到SRAM中!
使用FSMC读写数据的流程:
定义FSMC数据线对应的地址
//FSMC_Bank1_NORSRAM
#define FSMC_Addr_DATA ( ( uint32_t ) 0x60020000 )
如果要写数据,直接给这个地址赋值就可以:
/**
* @brief SRAM写入数据
* @param usData :要写入的数据
* @retval 无
*/
void SRAM_Write_Data ( uint16_t usData )
{
* ( __IO uint16_t * ) ( FSMC_Addr_DATA ) = usData;
}
如果要从存储器中读取数据,直接读取这个地址就可以:
/**
* @brief 从SRAM读取数据
* @param 无
* @retval 读取到的数据
*/
uint16_t SRAM_Read_Data ( void )
{
return ( * ( __IO uint16_t * ) ( FSMC_Addr_DATA ) );
}
uint16_t usR=0;
usR = SRAM_Read_Data (); /* READ DATA*/
这样极大的方便了我们的程序编写,其实这就好比软件IIC、SPI 跟硬件IIC、SPI 一样,我们不需要关心协议本身的时序,这些都由STM32内部来设置,只需要发送和读取数据即可,STM32跟外部存储器通信,使用硬件方式就是FMC,这样就很容易理解了。
STM32F1系列的芯片不支持扩展SDRAM (STM32F4跟H7支持),它仅支持使用FSMC外设扩展SRAM,由于引脚数量的限制,只有STM32F103ZE 或以上型号的芯片才可以扩展外部SRAM。
FSMC框图
如图所示,F1的FSMC包含四个主要模块:AHB总线(包含FSMC配置寄存器);HCLK时钟;NOR闪存和PSRAM控制器;NAND闪存和PC卡控制器。
FMC引脚说明
在框图的右侧是FSMC外设相关的控制引脚,控制不同类型存储器的时候会有一些不同的引脚,其中地址线FSMC_A和数据线FSMC_D是所有控制器都共用的。
NOR/SRAM信号 :这部分是NOR 和SRAM这类存储器的控制信号,其中:
- FSMC_CLK是时钟信号
- FSMC_NE[4:0]是片选信号 选择SRAM的不同存储区域
- FSMC_NBL[1:0] 是数据掩码
- FSMC_NL是输入地址是否有效
再加上下面的公共信号,组成了SRAM的控制引脚:
FMC地址映射
FSMC 连接好外部的存储器并初始化后,就可以直接通过访问地址来读写数据,其中这部分在内存中有着固定的存储地址,存储单元是映射到 STM32 的内部寻址空间的;在程序里,定义一个指向这些地址的指针,然后就可以通过指针直接修改该存储单元的内容,FSMC 外设会自动完成数据访问过程,读写命令之类的操作不需要程序控制,具体如下:
也就是从0x6000 0000 到0x9FFF FFFF 这1.0GB大小的空间被作为FMC的地址。
如果从内核看的话,左侧的是 Cortex-M3 内核的存储空间分配,右侧是 STM32 FSMC 外设的地址映射。可以看到 FSMC 的 NOR/PSRAM/SRAM/NAND FLASH 以及 PC 卡的地址都在 External RAM 地址空间内。正是因为存在这样的地址映射,使得访问 FSMC 控制的存储器时,就跟访问 STM32 的片上外设寄存器一样(片上外设的地址映射即图中左侧的“Peripheral”区域)。类似于访问固定的地址,跟寄存器操作异曲同工。
可以看到,FSMC 把整个 存储区域分成了 4 个 Bank 区域,NOR 及 SRAM 存储器只能使用 Bank1 的地址,在每个 Bank 的内部又分成了 4 个小块,每个小块有相应的控制引脚用于连接片选信号FSMC_NE1/2/3/4
当 STM32 访问0x6C000000-0x6FFFFFFF 地址空间时,其实就是访问FSMC BANK1的第一块区域 ,FSMC_NE1 引脚会自动设置为低电平。
具体是选择哪个小快,由地址线(ADDR[27:26])寻址确定。
Bank1的256M字节空间由28根地址线(ADDR[27:0])寻址。这里ADDR 是内部AHB地址总线,其中ADDR[25:0]对应外部存储器地址FSMC_A[25:0],而HADDR[26:27]对4个区进行寻址。
也就是ADDR 28位地址线的头两位,是选择哪个分区,后26位(bit)是选择对应64M个byte(字节)地址。
四个区对应二进制:
Bank1:0110-0000 0000-0000 0000-0000 0000-0000 ,即 60 00 00 00;
Bank2:0110-0100 0000-0000 0000-0000 0000-0000 ,即 64 00 00 00;
Bank3:0110-1000 0000-0000 0000-0000 0000-0000 ,即 68 00 00 00;
Bank4:0110-1100 0000-0000 0000-0000 0000-0000 ,即 6c 00 00 00;
FSMC不同位宽操作
FSMC总线除了复用到具体引脚上的FSMC_A[25:0],共计26路地址以外,还有两条内部总线ADDR[27:26]。通过这两路线才区分出了FMC_NE1,FMC_NE2,FMC_NE3和FMC_NE4。即0x60xx xxxx,0x64xx xxxx,0x68xx xxx和0x6Cxx xxxx。
这里要注意的是FSMC中每一个地址,对应的是存储器中的一个字节
假设用BANK1的区1,那么0x6000 0000-0x63ff ffff (共64M个字节byte地址) 对应的就是存储器的地址就是64Mx8=512M地址。 也就是FSMC的64M个bit地址映射着存储器64M个字节byte地址。
那也就是默认情况下存储器地址数据为8位,可以正常读取,但是如果存储器地址数据为16位,32位,就是两个字节一个地址,四个字节一个地址,那么控制16位,32位宽度的存储设备,且不支持单字节访问就比较麻烦了。**比如16位宽度的NOR Flash,写入仅支持16位或者32位(通过写入两次16位),写入8位数据是不支持的 **。
这样的话,我们本来对应64M的字节byte地址,就变成了32M的双字节byte地址,也就是最后一位无效了,因为地址一次要加2。
这个时候,为了方便操作,FMC在硬件设计上就提供了一种解决办法,将内部数据总线ADDR[25:0]措位后接到FMC_A地址引脚上,“丢弃”地址的bit-0,从bit-1开始对地址计数(相当于只计算偶数地址,总共有32M个地址)。
FSMC在实际输出地址时,将地址的值右移一位(相当于除以2,变成了偶数地址),输出到实际的地址线上。F103的文档上是这么写的:
如果是存储器宽度是32位的话,就是4个字节一个地址,就是右移两位
最终就是配置外部存储器的宽度为16位, FMC将使用内部的ADDR[25:1]地址来作为对外部存储器的寻址地址FMC_A[24:0]。如果存储器宽度为32位, FMC将使用内部的ADDR[25:2]地址进行外部寻址。
FSMC寄存器
上图中的第三部分,FSMC是由AHB总线控制配置寄存器来设置的。
NOR/PSRAM/SRAM 设备使用相同的控制器,NAND/PC 卡设备使用相同的控制器,不同的控制器有专用的寄存器用于配置其工作模式。
控制 NOR FLASH 的有 FSMC_BCR1/2/3/4 控制寄存器、FSMC_BTR1/2/3/4 片选时序寄存器以及 FSMC_BWTR1/2/3/4 写时序寄存器。 每种寄存器都有 4 个,分别对应于 4 个不同的存储区域,各种寄存器介绍如下:
- FSMC_BCR 控制寄存器可配置要控制的存储器类型、数据线宽度以及信号有效极性能参数。
- FMC_BTR 时序寄存器用于配置 SRAM 访问时的各种时间延迟,如数据保持时间、地址保持时间等。
- FMC_BWTR 写时序寄存器与 FMC_BTR 寄存器控制的参数类似,它专门用于控制写时序的时间参数。
FSMC时钟
FSMC 外设挂载在 AHB 总线上,时钟信号来自于 HCLK(默认 72MHz),控制器的同步时钟输出就是由它分频得到。 它的时钟频率可通过 FSMC_BTR 寄存器的 CLKDIV 位配置,HCLK 与 FSMC_CLK 的分频系数(CLKDIV),可以为 2~16 分频
-
它可用于与同步类型的 NOR FLASH 芯片通过FSMC_CLK 引脚输出进行同步通讯,
-
对于异步类型的存储器,不使用同步时钟信号,所以时钟分频配置不起作用。
FSMC 四种模式
对于存储器来说,可以分为带时钟信号的同步存储器和不带时钟信号的异步存储器
- 同步突发访问中获得第 1 个数据所需要的等待延迟(DATLAT)
- 异步突发访问方式,FSMC 主要设置 3 个时间参数:地址建立时间(ADDSET)、数据建立时间(DATAST)和地址保持时间(ADDHLD)。
FSMC 外设支持输出多种不同的时序以便于控制不同的存储器,综合了 SRAM/ROM、PSRAM 和 NOR Flash 产品的信号特点,定义了 ABCD 四种不同的异步时序模型。选用不同的时序模型时,需要设置不同的时序参数。
???
FSMC参数设置
我们知道NOR FLASH跟SRAM等存储器除了地址线跟数据线还有其他的控制线跟读写时序,在STM32中,这些时序都很贴心的为我们提供了设置方式,可以很好地匹配不同型号的存储器。
来看下不同模式的时序图,通过时序图来看下FSMC具体的流程:
模式1读操作
- NOE 是存储器的读信号线,N表示低电平有效,O表示output,E表示enable,
- NWE 是存储器的写信号线,N表示低电平有效,W表示write,E表示enable,
写存储器时,用NWE输出一个低电平,NOE保持高电平,读存储器时,用NOE输出一个低电平,NWE保持高电平
模式1写操作
模式A与模式1的区别是NOE的变化和相互独立的读写时序。
FSMC 控制异步 NOR FLASH 的时序
以控制异步 NOR FLASH 使用的模式 B 进行讲解
FSMC 读 NOR FLASH 的时序图:
以读时序为例,该图表示一个存储器读操作周期由地址建立周期(ADDSET)、数据建立周期(DATAST)以及 2 个 HCLK 周期组成。
- 在地址建立周期中,地址线发出要访问的地址,数据掩码信号线指示出要读取地址的高、低字节部分,片选信号使能存储器芯片;
- 地址建立周期结束后读使能信号线发出读使能信号,接着存储器通过数据信号线把目标数据传输 给 FSMC,FSMC 把它交给内核;
FSMC 写 NOR FLASH 的时序图:
写时序类似,区别是它的一个存储器操作周期仅由地址建立周期(ADDSET)和数据建
立周期(DATAST)组成,且在数据建立周期期间写使能信号线发出写信号,接着 FSMC 把
数据通过数据线传输到存储器中。
当 FSMC 外设被配置成正常工作,并且外部接了 NOR FLASH 时,若向 0x60000000 地址写入数据如 0xABCD,FSMC 会自动在各信号线上产生相应的电平信号,写入数据。FSMC 会控制片选信号 NE1 输出低电平选择相应的 NOR 芯片,然后使用地址线 A[25:0]输出0x60000000,在 NWE 写使能信号线上发出低电平的写使能信号,而要写入的数据信号0xABCD 则从数据线 D[15:0]输出,然后数据就被保存到 NOR FLASH 中了。
FSMC的功能
FSMC的功能总结:将AHB传输信号转换到适当的外部设备协议;满足访问外部设备的时序要求。 所有的外部存储器共享控制器输出的地址、数据和控制信号,每个外部设备可以通过一个唯一的片选信号加以区分。FSMC在任一时刻只访问一个外部设备。
FSMC的特点
1、 FSMC的一大特点是支持不同位宽的异步读写操作。
2、 FSMC的映射地址空间中,不同的BANK是独立的,可用于扩展不同类型的存储器。当FSMC同时使用多个外部存储器时,FSMC会通过总线悬空延时时间参数,来防止访问冲突发生。
3、 支持代码从FSMC扩展的外部存储器中直接运行。不需要首先调入内部SRAM。
库函数中的FSMC:
如果是F1系列:FSMC 相关的库函数分布在 stm32f1xx_II_fsmc.c 文件和头文件stm32f1xx_II_fsmc.h 中。
如果是F4系列:FSMC 相关的库函数分布在 stm32f4xx_fsmc.c 文件和头文件 stm32f4xx_fsmc.h 中。
在.h文件中,一共有几个结构体:
FSMC_NORSRAMTimingInitTypeDef类型
FSMC_NORSRAMInitTypeDef类型
FSMC_NAND_PCCARDTimingInitTypeDef类型
FSMC_NANDInitTypeDef类型
FSMC_PCCARDInitTypeDef类型
以HAL库配置讲解
FSMC 接口支持多种存储器,包括 SDRAM,NOR,NAND 和 PC CARD等。HAL 库为每种支持的存储器类型都定义了一个独立的 HAL 库文件,并且在文件中定义了独立的初始化函数,这里列出几种存储器的初始化函数:
HAL_SRAM_Init(); //SRAM 初始化函数,省略入口参数
HAL_SDRAM_Init(); //SDRAM 初始化函数,省略入口参数
HAL_NOR_Init(); //NOR 初始化函数,省略入口参数
HAL_NAND_Init(); //NAND 初始化函数,省略入口参数
异步SRAM
HAL库来说下具体的初始化流程:
HAL库中函数配置在stm32f1xx_hal_sram.c跟stm32f1xx_hal_sram.h中
这两个文件对stm32f1xx_II_fsmc.c跟stm32f1xx_II_fsmc.h中的函数做了封装,然后生成了HAL库
FSMC初始化顺序
这里我们先说下FSMC的总体初始化顺序:
- 使能 FSMC 和 GPIO 时钟,初始化 IO 口配置,设置映射关系
- 设置FSMC基本参数初始化
- 设置FSMC读时序/写时序初始化
- FSMC初始化函数
- FSMC硬件层初始化
FSMC硬件层初始化
STM32的管脚排列很没有规律,而且分布在多个不同端口上,初始化要十分小心.需要用到的引脚都要先初始化成”复用功能推挽输出”模式(GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP )。
并且开启时钟 (RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);
使能 FSMC 和 GPIO 时钟,初始化 IO 口配置,设置映射关系。 IO口的使能引脚较多,看引脚模式配置即可。
FSMC 时钟使能:
__HAL_RCC_FSMC_CLK_ENABLE(); //使能 FSMC 时钟
void FSMC_SRAM_MspInit(void)
{
GPIO_InitTypeDef GPIO_Init_Structure;
/* Enable FMC clock */
__HAL_RCC_FSMC_CLK_ENABLE();
/* Enable GPIOs clock */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/* Common GPIO configuration */
GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
GPIO_Init_Structure.Pull = GPIO_PULLUP;
GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_HIGH;
/* 地址线:F0---F5,F12---F15,G0---G5,D11---D13;
数据信号线;D0,D1,D8---D10,D14,D15,E7---E15;
CS片选:G10;
WE写使能:D5;
OE读使能;D4;
UB数据掩码;E1;
LB数据掩码;E0;
*/
/* GPIOD configuration */
GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 |\
GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 |\
GPIO_PIN_14 | GPIO_PIN_15;
HAL_GPIO_Init(GPIOD, &GPIO_Init_Structure);
/* GPIOE configuration */
GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 |\
GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |\
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
HAL_GPIO_Init(GPIOE, &GPIO_Init_Structure);
/* GPIOF configuration */
GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4 |\
GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
HAL_GPIO_Init(GPIOF, &GPIO_Init_Structure);
/* GPIOG configuration */
GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4 |\
GPIO_PIN_5 | GPIO_PIN_10;
HAL_GPIO_Init(GPIOG, &GPIO_Init_Structure);
}
初始化 FMC 接口读写时序参数
在完成硬件的MSP初始化之后,就要初始化SRAM了,使用的函数为 HAL_SRAM_Init:
HAL_SRAM_Init(SRAM_HandleTypeDef *hsram,
FSMC_NORSRAM_TimingTypeDef *Timing,
FSMC_NORSRAM_TimingTypeDef *ExtTiming;
)
该函数有三个入口参数:
- hsram,它是SRAM_HandleTypeDef 结构体指针类型 设置SRAM的基本参数.
- Timing 设置NORSRAM存储器的读时序.
- ExtTiming 设置NORSRAM存储器的写时序.
初始化 FMC 接口基本参数
我们使用的结构体为:SRAM_HandleTypeDef.
结构体 SRAM_HandleTypeDef 定义如下:
typedef struct
{
FMC_NORSRAM_TypeDef *Instance; //寄存器基地址
FMC_NORSRAM_EXTENDED_TypeDef *Extended; //扩展模式寄存器基地址
FMC_NORSRAM_InitTypeDef Init;
HAL_LockTypeDef Lock;
__IO HAL_SRAM_StateTypeDef State;
DMA_HandleTypeDef *hdma;
SRAM_HandleTypeDef;
}
- Instance: 寄存器基地址,如果读写时序一样则配置基地址就行.
- Extended :扩展模式寄存器基地址 ,也就是读写时序不同的时候, 指定写操作时序寄存器地址.
- FMC_NORSRAM_InitTypeDef Init :是 FSMC_NORSRAM_InitTypeDef 结构体指针类型,是真正用来设置 SRAM 控制接口参数的.
- 成员变量 Lock 和 State 是 HAL 库处理状态标识变量.
- 成员变量 hdma 在使用 DMA 时候才使用.
也就是我们只需要关心前三个参数,最主要的是第三个Init ,我们来看下:
/**
* @brief FSMC NOR/SRAM Init structure definition
*/
typedef struct
{
uint32_t NSBank; /*设置要控制的 Bank 区域 */
uint32_t DataAddressMux; /*设置地址总线与数据总线是否复用 */
uint32_t MemoryType; /*设置存储器的类型 */
uint32_t MemoryDataWidth; /*设置存储器的数据宽度*/
uint32_t BurstAccessMode; /*设置是否支持突发访问模式,只支持同步类型的存储器 */
uint32_t WaitSignalPolarity; /*设置等待信号的极性*/
uint32_t WrapMode; /*设置是否支持对齐的突发模式 */
uint32_t WaitSignalActive; /*配置等待信号在等待前有效还是等待期间有效 */
uint32_t WriteOperation; /*设置是否写使能 */
uint32_t WaitSignal; /*设置是否使能等待状态插入 */
uint32_t ExtendedMode; /*设置是否使能扩展模式 */
uint32_t WriteBurst; /*设置是否使能写突发操作*/
uint32_t AsynchronousWait; /*设置是否使能等待信号*/
uint32_t PageSize; /*指定页的大小*/
} FSMC_NORSRAM_InitTypeDef;
-
NSBank用来指定使用到的存储块区号, -
DataAddressMux用来设置是否使能地址/数据复用,该变量仅对 NOR/PSRAM 有 效 -
MemoryType用来设置存储器类型 -
MemoryDataWidth用来设置存储器数据总线宽度,可选 8 位还是 16 位, -
WriteOperation用来设置存储器写使能,也就是是否允许写入。毫无疑问我们会进行存储器写操作 -
ExtendedMode用来设置是否使能扩展模式,也就是是否允许读写使用不同时序 -
ContinuousClock用来设置启用/禁止 FSMC 时钟输出到外部存储设备 ,
其他参数 WriteBurst,BurstAccessMode,WaitSignalPolarity,WrapMode,WaitSignalActive,WaitSignal,AsynchronousWait 等是用在突发访问和同步时序情况下,这里不过多讲解。
在stm32f1xx_II_fsmc.h中的每个参数的配置如下:
- uint32_t NSBank
用于设置使用的 BANK,片选 NE1 对于 BANK1,以此类推.
#define FSMC_NORSRAM_BANK1 ((uint32_t)0x00000000U)
#define FSMC_NORSRAM_BANK2 ((uint32_t)0x00000002U)
#define FSMC_NORSRAM_BANK3 ((uint32_t)0x00000004U)
#define FSMC_NORSRAM_BANK4 ((uint32_t)0x00000006U)
- uint32_t DataAddressMux
用于设置地址线和数据线复用,可以选择使能或者禁止:
#define FSMC_DATA_ADDRESS_MUX_DISABLE ((uint32_t)0x00000000U)
#define FSMC_DATA_ADDRESS_MUX_ENABLE ((uint32_t)0x00000002U)
- uint32_t MemoryType
用于设置使用的存储器类型,具体支持的参数如下:
#define FSMC_MEMORY_TYPE_SRAM ((uint32_t)0x00000000U)
#define FSMC_MEMORY_TYPE_PSRAM ((uint32_t)0x00000004U)
#define FSMC_MEMORY_TYPE_NOR ((uint32_t)0x00000008U)
- uint32_t MemoryDataWidth
用于设置外接的存储器位宽,具体支持的参数如下:
#define FSMC_NORSRAM_MEM_BUS_WIDTH_8 ((uint32_t)0x00000000U)
#define FSMC_NORSRAM_MEM_BUS_WIDTH_16 ((uint32_t)0x00000010U)
#define FSMC_NORSRAM_MEM_BUS_WIDTH_32 ((uint32_t)0x00000020U)
- uint32_t BurstAccessMode
用于使能或者禁止突发模式,仅用于支持同步突发的存储器,具体支持的参数如下:
#define FSMC_BURST_ACCESS_MODE_DISABLE ((uint32_t)0x00000000U)
#define FSMC_BURST_ACCESS_MODE_ENABLE ((uint32_t)0x00000100U)
- uint32_t WaitSignalPolarity
用于设置等待信号的极性,仅当使能突发模式时有效,具体支持的参数如下:
#define FSMC_WAIT_SIGNAL_POLARITY_LOW ((uint32_t)0x00000000U)
#define FSMC_WAIT_SIGNAL_POLARITY_HIGH ((uint32_t)0x00000200U)
- uint32_t WrapMode
设置是否支持对齐的突发模式,如果不是突发模式就不需要设置
#define FSMC_WRAP_MODE_DISABLE ((uint32_t)0x00000000U)
#define FSMC_WRAP_MODE_ENABLE ((uint32_t)0x00000400U)
- uint32_t WaitSignalActive
用于在等待状态之前或等待状态期间,存储器是否在一个时钟周期内置位等待信号,仅当使能突发模式时有效,具体支持的参数如下:
#define FSMC_WAIT_TIMING_BEFORE_WS ((uint32_t)0x00000000U)
#define FSMC_WAIT_TIMING_DURING_WS ((uint32_t)0x00000800U)
- uint32_t WriteOperation
用于使能或者禁止写保护,具体支持的参数如下:
#define FSMC_WRITE_OPERATION_DISABLE ((uint32_t)0x00000000U)
#define FSMC_WRITE_OPERATION_ENABLE ((uint32_t)0x00001000U)
- uint32_t WaitSignal
用于使能或者禁止通过等待信号来插入等待状态,仅当使能突发模式时有效,具体支持的参数如下:
#define FSMC_WAIT_SIGNAL_DISABLE ((uint32_t)0x00000000U)
#define FSMC_WAIT_SIGNAL_ENABLE ((uint32_t)0x00002000U)
- uint32_t ExtendedMode
用于使能或者禁止扩展模式,具体支持的参数如下:
#define FSMC_EXTENDED_MODE_DISABLE ((uint32_t)0x00000000U)
#define FSMC_EXTENDED_MODE_ENABLE ((uint32_t)0x00004000U)
- uint32_t WriteBurst
用于使能或者禁止异步的写突发操作:
#define FSMC_WRITE_BURST_DISABLE ((uint32_t)0x00000000U)
#define FSMC_WRITE_BURST_ENABLE ((uint32_t)0x00080000U)
- uint32_t AsynchronousWait
用于异步传输期间,使能或者禁止等待信号,仅操作异步存储器有效,具体支持的参数如下:
#define FSMC_ASYNCHRONOUS_WAIT_DISABLE ((uint32_t)0x00000000U)
#define FSMC_ASYNCHRONOUS_WAIT_ENABLE ((uint32_t)0x00008000U)
- uint32_t PageSize
用于设置页大小,FSMC 操作器件 Cellular RAM 1.5 时要用到,具体支持的参数如下:
#define FSMC_PAGE_SIZE_NONE ((uint32_t)0x00000000U)
#define FSMC_PAGE_SIZE_128 ((uint32_t)0x00010000U)
#define FSMC_PAGE_SIZE_256 ((uint32_t)0x00020000U)
#define FSMC_PAGE_SIZE_512 ((uint32_t)0x00030000U)
#define FSMC_PAGE_SIZE_1024 ((uint32_t)0x00040000U)
初始化 FMC 读写时序
2个参数Timing和ExtTiming,它们都是FSMC_NORSRAM_TimingTypeDef结构体指针类型,分别用来设置 FMC 接口读和写时序,主要涉及地址建立保持时间,数据建立时间等等配置,
如果是异步存储器,读写时序不一样,读写速度要求不一样,参数Timing 和 ExtTiming 需要设置了不同的值。
如果是同步存储器,读写时序设置一致就可.
FMC_NORSRAM_TimingTypeDef 结构体定义如下:
typedef struct
{
uint32_t AddressSetupTime; //地址建立时间
uint32_t AddressHoldTime; //地址保持时间
uint32_t DataSetupTime; //数据建立时间
uint32_t BusTurnAroundDuration; //总线恢复时间
uint32_t CLKDivision; // 时钟分频因子
uint32_t DataLatency; //同步突发 NOR FLASH 的数据延迟
uint32_t AccessMode; //异步模式配置
FSMC_NORSRAM_TimingTypeDef;
}
- AccessMode
- 模式设置
#define FSMC_ACCESS_MODE_A ((uint32_t)0x00000000U)
#define FSMC_ACCESS_MODE_B ((uint32_t)0x10000000U)
#define FSMC_ACCESS_MODE_C ((uint32_t)0x20000000U)
#define FSMC_ACCESS_MODE_D ((uint32_t)0x30000000U)
最终初始化代码
/**
* @brief Initializes the SRAM device.
* @retval SRAM status
*/
void FSMC_SRAM_Init(void)
{
//结构体句柄初始化
SRAM_HandleTypeDef SRAM_Handler;
FSMC_NORSRAM_TimingTypeDef FSMC_ReadWriteTim;
FSMC_NORSRAM_TimingTypeDef FSMC_WriteTim;
//FSMC初始化寄存器
SRAM_Handler.Instance = FSMC_NORSRAM_DEVICE;
SRAM_Handler.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
SRAM_Handler.Init.NSBank=FSMC_NORSRAM_BANK4; //使用NE4
SRAM_Handler.Init.DataAddressMux=FSMC_DATA_ADDRESS_MUX_DISABLE; //地址/数据线不复用
SRAM_Handler.Init.MemoryType=FSMC_MEMORY_TYPE_SRAM; //SRAM
SRAM_Handler.Init.MemoryDataWidth=FSMC_NORSRAM_MEM_BUS_WIDTH_16; //16位数据宽度
SRAM_Handler.Init.BurstAccessMode=FSMC_BURST_ACCESS_MODE_DISABLE; //是否使能突发访问,仅对同步突发存储器有效,此处未用到
SRAM_Handler.Init.WaitSignalPolarity=FSMC_WAIT_SIGNAL_POLARITY_LOW; //等待信号的极性,仅在突发模式访问下有用
SRAM_Handler.Init.WaitSignalActive=FSMC_WAIT_TIMING_BEFORE_WS; //存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT
SRAM_Handler.Init.WriteOperation=FSMC_WRITE_OPERATION_ENABLE; //存储器写使能
SRAM_Handler.Init.WaitSignal=FSMC_WAIT_SIGNAL_DISABLE; //等待使能位,此处未用到
SRAM_Handler.Init.ExtendedMode=FSMC_EXTENDED_MODE_DISABLE; //读写使用相同的时序
SRAM_Handler.Init.AsynchronousWait=FSMC_ASYNCHRONOUS_WAIT_DISABLE; //是否使能同步传输模式下的等待信号,此处未用到
SRAM_Handler.Init.WriteBurst=FSMC_WRITE_BURST_DISABLE; //禁止突发写
//FSMC读时序控制寄存器
FSMC_ReadWriteTim.AddressSetupTime=0x06; //地址建立时间(ADDSET)为7个HCLK 13.8ns*7=96.6ns
FSMC_ReadWriteTim.AddressHoldTime=0;
FSMC_ReadWriteTim.DataSetupTime=26; //数据保存时间为27个HCLK =13.8*27=372.6ns
FSMC_ReadWriteTim.AccessMode=FSMC_ACCESS_MODE_A; //模式A
//FSMC写时序控制寄存器
FSMC_WriteTim.BusTurnAroundDuration=0; //总线周转阶段持续时间为0,此变量不赋值的话会莫名其妙的自动修改为4。导致程序运行正常
FSMC_WriteTim.AddressSetupTime=3; //地址建立时间(ADDSET)为4个HCLK =55.2ns
FSMC_WriteTim.AddressHoldTime=0;
FSMC_WriteTim.DataSetupTime=0x06; //数据保存时间为13.8ns*7个HCLK=96.6ns
FSMC_WriteTim.AccessMode=FSMC_ACCESS_MODE_A; //模式A
/* SRAM controller initialization */
FSMC_SRAM_MspInit();
HAL_SRAM_Init(& SRAM_Handler, &FSMC_ReadWriteTim, &FSMC_WriteTim);
}
———————————————— 版权声明:本文为CSDN博主「Z小旋」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/as480133937…
门禁系统设计与实现(以125kHz门禁阅读器为例)
硬件设计
门禁阅读器硬件结构
如上图所示是一个通用只读125kHz RFID阅读器的板级模块.
核心模块
如上图所示是一个
M106BWNL-34模块和引脚设置
- 支持EM、TK及其兼容卡片。
- 超小体积,尺寸仅为32.5mm×17.5mm。
- 低功耗,读卡电流为29mA。
- 基于模块的扩展功能很强,可根据用户要求修改软件、定制个性化模块,而不用改变线路板。
- 通用协议:
- UART:适用于PC或8位UART的单片机,波特率可选。
- 支持韦根-34:通用读卡器接口。
- 自带看门狗。
- 供电电源控制和指示。
M106BWNL-34模块有20个引脚,用到的引脚说明如下表所示。
外围电路
外围电路进一步完善接口支持,提供了板载天线和供电控制电路,并有电源供电指示LED等外围支持电路。简化了应用接口,实现了模块化即插即用. 阅读器模块供电电源由VCCN 125K进行控制,并由LED 125K进行电源指示。TX1、TX2天线接口与板载天线两端相连,阅读器模块通过WG1、WG2两根数据线实现与MCU通信的韦根接口,进行数据传输。
板级模块接口
此板级模块可选择使用3.3V或5V供电电源,通过跳线进行选择。其中,IF1、IF2是板外围接口端,是阅读器韦根接口信号及电源控制信号端。
外部接口设置
软件设计
软件设计重点包括:载波的产生和门禁射频卡的解码。载波信号产生相对简单。解码软件设计相对较复杂,要对门禁射频卡进行解码,首先应掌握门禁射频卡的存储格式和数据编码方式。在实现门禁射频卡解码的基础上我们可以进一步设计实现门禁系统的其他功能。
基本功能设计
基本功能主要有:系统初始化,阅读器模块开/关等。相应功能实现代码如下: