前言
最近遇到一个案子,需要使用到 MM32F5270 FSMC 的地址数据复用功能,所谓地址数据复用功能,就是将地址线的前 16bit 与数据线公共用,这样可以减少使用 FSMC 的信号线的数量。使其可以在更小封装的芯片上使用 FSMC 功能,并且降低 PCB 布线的难度,但数据地址复用功能也需要外设模块的支持才行,如果外设模块不支持地址数据复用的功能,则需要使用一个锁存器来暂存地址信息,这样反而增加了 PCB 布线难度,增加了产品的成本。
本案需要使用 FSMC 以访问 NOR Flash 的方式,使用地址数据复用的功能访问外设模块:使用 FSMC 连接外设模块后,相当于将外设模块的一块地址空间映射到微控制器指定的一块地址空间中,访问这块地址空间,会使 FSMC 发起访问请求,通过外部连接的总线与外设模块进行通信,这样一来,就相当于将外设模块当作微控制器内部的一块外设寄存器,操作外设模块就如同操作微控制器内部的 UART,SPI 那样方便;由于使用到数据地址复用功能,且该外设模块的地址宽度小于 16bit,只能 32bit 方式访问,因此只需要将 FSMC 的片选信号线,读写控制信号线,地址数据复用控制信号线,数据信号线连接即可,无需连接地址信号线,以及 NBLx 信号线。
但数据地址复用功能不同于非复用方式访问外设模块,需要额外进行一些配置,本文将讲述如何在 MM32F5270 上使用 FSMC 的数据地址功能。
操作
使用 FSMC 的地址复用功能,除了配置 FSMC 的相关参数外,还需要再 SYSCFG 中进行如下操作:
在 RCC 中打开 SYSCFG 的时钟
配置 SYSCFG->CFGR1[FC_ODATAEN] 为0,在本案中,对方接口类型为 NOR Flash 类型,因此还需要将 SYSCFG->CFGR1[FC_MODESEL] 配置为1。
对应的操作如下:
clock_init.c 中的 BOARD_InitBootClocks() 中加入如下内容:
void BOARD_InitBootClocks(void)
{
......
RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_SYSCFG, true);
RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
......
}
由于使用地址复用功能的话需要额外使用地址数据复用控制信号线 (NADV),对应的引脚为 PB7 引脚,所以需要在 RCC 的使能 GPIOB 的时钟,并且将 PB7 配置为 NADV 的功能,修改 pin_init.c 的 BOARD_InitPins():
void BOARD_InitPins(void)
{
GPIO_Init_Type gpio_init;
......
gpio_init.Pins = GPIO_PIN_7;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_12);
......
}
在 RCC 中打开 SYSCFG 的时钟后,就可以修改 SYSCFG 的配置了,修改 SYSCFG 需要引入头文件 “hal_syscfg.h”:
#include "hal_syscfg.h"
......
SYSCFG_SetFSMCMode(SYSCFG_FSMCMode_NorFlash);
SYSCFG_SetFSMCPinUseMode(SYSCFG_FSMCPinUseMode_DataAndAddrMixed);
......
解决了数据地址复用的问题外,这个案子还涉及到了 FSMC brust 传输的问题:当 FMSC 数据总线配置为 16bit 时,CPU 进行 32bit 访问,或对 FSMC 的一块区域进行批量访问时,就会触发 brust 传输,在 brust 传输下,片选信号会一直保持选中的状态,直到传输完成,这样可以加快访问速度。但本案通过 FMSC 对接的外设模块不支持这种访问方式,使用 brust 传输会造成通信异常,读写的数据与期望值不一致,为解决这个问题,当对外设进行 32bit 访问时,需要对执行流进行约束:
#define MODULE_INTERFACE(offset) ((__IO uint16_t*)(FSMC_BANK0_BASE + (offset)))
#define MODULE_INTERFACE_READ32(offset) (*MODULE_INTERFACE((addr) & (~0x03)) | (*MODULE_INTERFACE(((addr) & (~0x03)) + 2) << 16))
#define MODULE_INTERFACE_WRITE32(offset, val) do { \
*MODULE_INTERFACE( (offset) & (~0x03) ) = (uint16_t)(val & 0x0000FFFF); \
*MODULE_INTERFACE(((offset) & (~0x03)) + 2) = (uint16_t)(val >> 16); \
} while(0)
通过上述的 MODULE_INTERFACE_READ32() 宏进行 32bit 读访问 和 MODULE_INTERFACE_WRITE32 宏进行 32bit 写访问,而不直接使用 (__IO uint32_t*) 的方式进行读写操作,避免了 FSMC 产生 brust 传输,满足外设模块的时序要求。
这里需要强调一点,FSMC 在 16bit 总线上进行 32bit 访问时,是先传输低 16bit 内容,再传输高 16bit 内容,因此在上述的宏中,将访问低 16bit 的操作放在前面,高 16bit 的操作放在后面,顺序执行。
进行如上操作,通过 FSMC 进行写操作,得到如图1的波形:
图1 FSMC 数据地址复用波形图
NADV 低电平表示数据总线内容为地址信息,从图1中可以看出第一次 NADV 低电平时, DA0 ~ DA5 全为低电平(后续 DAx 不再展示),NADV 拉高后一段时间数据数据信号线电平发生变化,NWE 信号线也变为低电平,进行写操作。
第二次 NADV 低电平时,DA0 为高电平,由于数据位宽为 16bit,因此这里代表向地址2进行写操作,NADV 高电平后,DA0 很快发生了电平变化,变化为数据内容。
后面第三次,第四次 NADV 低电平的时候,都可以看到 DAx 发生变化,由此证明,数据地址复用功能生效,使用该方法访问外设模块时,也能够正常访问。
总结
在开发 FSMC 应用时,逻辑分析仪是必不可少的工具,示波器虽然能够更加精确地表达信号线的电平变化,但较少的通道数量无法满足动辄几十根信号线的 FSMC 应用。
使用 FSMC 的地址数据复用功能,除了对 FSMC 进行操作外,需要对 SYSCFG 进行操作,在操作 SYSCFG 前,还需要再 RCC 中使能 SYSCFG 的时钟。
PB7 为 NADV 信号线,是在数据地址复用时必不可少的信号线,因此将普通的 FSMC 用法修改为数据地址复用的方式时,还需要对 PB7 进行配置。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/UnsicentificLLLL/article/details/132187585
|