MM32F5270 FSMC 数据地址复用

MM32F5270 FSMC 数据地址复用

前言

最近遇到一个案子,需要使用到 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 进行配置。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以帮您编写一个 MM32F5270 驱动 RC522 外设的代码。以下是一个简单的示例代码,用于初始化并与 RC522 进行通信。请注意,这只是一个基本框架,您可能需要根据您的具体需求进行修改和完善。 ```c #include "mm32f5270.h" #include "rc522.h" // RC522 接口定义 #define RC522_SPI_PORT SPI1 #define RC522_SPI_CLK RCC_APB2Periph_SPI1 #define RC522_SPI_GPIO_PORT GPIOA #define RC522_SPI_CS_PIN GPIO_Pin_4 #define RC522_SPI_SCK_PIN GPIO_Pin_5 #define RC522_SPI_MISO_PIN GPIO_Pin_6 #define RC522_SPI_MOSI_PIN GPIO_Pin_7 // RC522 初始化 void RC522_Init(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能 SPI 时钟 RCC_APB2PeriphClockCmd(RC522_SPI_CLK, ENABLE); // 配置 SPI 引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = RC522_SPI_SCK_PIN; GPIO_Init(RC522_SPI_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RC522_SPI_MISO_PIN; GPIO_Init(RC522_SPI_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RC522_SPI_MOSI_PIN; GPIO_Init(RC522_SPI_GPIO_PORT, &GPIO_InitStructure); // 配置 CS 引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = RC522_SPI_CS_PIN; GPIO_Init(RC522_SPI_GPIO_PORT, &GPIO_InitStructure); // 初始化 SPI 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_256; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(RC522_SPI_PORT, &SPI_InitStructure); SPI_Cmd(RC522_SPI_PORT, ENABLE); } // RC522 发送数据 void RC522_SendByte(uint8_t data) { while (SPI_I2S_GetFlagStatus(RC522_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(RC522_SPI_PORT, data); while (SPI_I2S_GetFlagStatus(RC522_SPI_PORT, SPI_I2S_FLAG_RXNE) == RESET); SPI_I2S_ReceiveData(RC522_SPI_PORT); } // RC522 接收数据 uint8_t RC522_ReceiveByte(void) { while (SPI_I2S_GetFlagStatus(RC522_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(RC522_SPI_PORT, 0xFF); while (SPI_I2S_GetFlagStatus(RC522_SPI_PORT, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(RC522_SPI_PORT); } // RC522 读取寄存器 uint8_t RC522_ReadReg(uint8_t addr) { uint8_t value; GPIO_ResetBits(RC522_SPI_GPIO_PORT, RC522_SPI_CS_PIN); RC522_SendByte((addr << 1) | 0x80); value = RC522_ReceiveByte(); GPIO_SetBits(RC522_SPI_GPIO_PORT, RC522_SPI_CS_PIN); return value; } // RC522 写入寄存器 void RC522_WriteReg(uint8_t addr, uint8_t value) { GPIO_ResetBits(RC522_SPI_GPIO_PORT, RC522_SPI_CS_PIN); RC522_SendByte(addr << 1); RC522_SendByte(value); GPIO_SetBits(RC522_SPI_GPIO_PORT, RC522_SPI_CS_PIN); } ``` 请注意,此代码仅涵盖了基本的初始化和读写寄存器操作。具体的功能和应用逻辑需要根据您的需求进行补充和修改。同时,还需要根据您的硬件连接情况进行相应的配置。 希望对您有所帮助!如有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值