1. 首页 > 电脑知识

FMC总线个人 拓展资料 fmc fsmc

作者:admin 更新时间:2025-06-14
摘要:接今年2月份简单总结的IIC、 SPI 总线的个人使用,把FMC 总线也简单记录下。 一、总线简介: STM32H7 中叫做 FMC总线;GD32H7 中 叫做 EXMC 总线。 STM32H7 的 FMC 总线是挂载 64 位带宽的 AXI 总线上,F1,F4 和 F7 是挂在 32 位总线上。 使用 FMC,可以用来外,FMC总线个人 拓展资料 fmc fsmc

 

  接今年2月份简单 拓展资料的IIC、 SPI 总线的个人使用,把FMC 总线也简单记录下。 一、总线简介:    STM32H7 中叫做 FMC总线;GD32H7 中 叫做 EXMC 总线。    STM32H7 的 FMC 总线是挂载 位带宽的 AXI 总线上,F1,F4 和 F7 是挂在 32 位总线上。 使用 FMC,可以用来外挂 NOR/PSRAM 型存储器,SRAM 型存储器,NAND 型存储器,SDRAM等存储器。支持8~32bit 总线带宽控制。   每个片选下的存储器空间配置都是独立的,有专门的寄存器,互不影响。这点比较重要,要牢记。( 下面内容硬件框图,来自STM32) 下面内容 地址区域分配(来自STM32) 二、个人应用:    总线外扩SDRAM。主控芯片使用的GD32H759,扩展芯片SDRAM 选用IS42S1 00J_5BLI, 见其手册说明:

4个BANKS; 内存 大致 Mb=8MB 字节; 自刷新模式(Self refresh),4096 次刷新每 ms (A1 ); CAS latency (2,3 clocks); 主频高达200MHz; 等等。

<1> 扩展芯片的管脚定义:

    本SDRAM 的数据线是16bit ,数据通讯时,16位数据是同步传输的。实际使用时,我们可能用到8bit 或者16bit ,即: 16bit 线不是所有时候都同步使用。 因此:用到了 LDQM线和UDQM线来配合,每根线对应8bit 数据。比如:LDQM为低电平,UDQM为高电平时,DQ[7:0]数据有效,DQ[15:8]数据无效。与MCU 硬件设计连接时,注意看下MCU 对应的两根线,不要接反,否则数据就错了。 ps:     这次的项目开发 经过,硬件设计这两根线与MCU -EXMC 的连接就反了, 结局是:16bit 数据读写正确,8bit 数据错了,我花了几天功夫帮硬件工程师排查到 缘故。

|<2> 原理图 以上原理图参考 GD32H759 开发板的设计,用的是另一款SDRAM 芯片,都类似。

<3> 驱动程序 我设计时用的IS42S1 00J_5BLI,时钟选150MHZ ,驱动实现如下:

void exmc_synchronous_dynamic_ram_init(uint32_t sdram_device) //150MHZ { exmc_sdram_parameter_struct sdram_init_struct; exmc_sdram_timing_parameter_struct sdram_timing_init_struct={ 0}; exmc_sdram_com nd_parameter_struct sdram_com nd_init_struct={ 0}; exmc_sdram_struct_para_init(&sdram_init_struct); uint32_t com nd_content = 0, bank_select; uint32_t timeout = SDRAM_TIMEOUT; rcu_exmc_clock_config(RCU_EXMCSRC_PLL1R); //选择PLL1R 作为SDRAM 的时钟源.//注意看MCU 手册的时钟树,EXMC的时钟源好几种选择。 /* enable EXMC clock */ rcu_periph_clock_enable(RCU_EXMC); rcu_periph_clock_enable(RCU_GPIOC); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_GPIOE); rcu_periph_clock_enable(RCU_GPIOF); rcu_periph_clock_enable(RCU_GPIOG); /*common GPIO configuration */ //-----------------------PORT-C--------------------------------------------------// /*SDNWE(PC0)*/ gpio_af_set(EXMC_SDNWE_PORT_C , GPIO_AF_12, EXMC_SDNWE_PIN); gpio_mode_set(EXMC_SDNWE_PORT_C , GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_SDNWE_PIN); gpio_output_options_set(EXMC_SDNWE_PORT_C , GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_SDNWE_PIN); /*SDNE0(PC2)*/ gpio_af_set(EXMC_SDNE0_PORT_C, GPIO_AF_12, EXMC_SDNE0_PIN); gpio_mode_set(EXMC_SDNE0_PORT_C, GPIO_MODE_AF,GPIO_PUPD_NONE, EXMC_SDNE0_PIN); gpio_output_options_set(EXMC_SDNE0_PORT_C, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_SDNE0_PIN); /*SDCKE0(PC5)*/ gpio_af_set(EXMC_SDCKE0_PORT_C, GPIO_AF_12, EXMC_SDCKE0_PIN); gpio_mode_set(EXMC_SDCKE0_PORT_C, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_SDCKE0_PIN); gpio_output_options_set(EXMC_SDCKE0_PORT_C, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_SDCKE0_PIN); /*D6(PC12)pin cfghj2onfiguration */ gpio_af_set(EXMC_DATA_PORT_C, GPIO_AF_1, EXMC_D6_PIN); gpio_mode_set(EXMC_DATA_PORT_C, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_D6_PIN); gpio_output_options_set(EXMC_DATA_PORT_C, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_D6_PIN); //-----------------------PORT-D--------------------------------------------------// /*D2(PD0),D3(PD1),D13-D15(PD8~PD10),D0(PD14),D1(PD15), pin configuration */ gpio_af_set(GPIOD, GPIO_AF_12, EXMC_D0_PIN| EXMC_D1_PIN| EXMC_D2_PIN | EXMC_D3_PIN| EXMC_D13_PIN| EXMC_D14_PIN| EXMC_D15_PIN); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_D0_PIN| EXMC_D1_PIN| EXMC_D2_PIN | EXMC_D3_PIN| EXMC_D13_PIN| EXMC_D14_PIN| EXMC_D15_PIN); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_D0_PIN| EXMC_D1_PIN| EXMC_D2_PIN | EXMC_D3_PIN| EXMC_D13_PIN| EXMC_D14_PIN| EXMC_D15_PIN); /*D7(PD2)*/ gpio_af_set(GPIOD, GPIO_AF_1,EXMC_D7_PIN); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_D7_PIN); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_D7_PIN); //-----------------------PORT-E--------------------------------------------------// /*NBL0(PE0),NBL1(PE1),D4(PE7),D5(PE8),D8(PE11),D9(PE12),D10(PE13),D11(PE14), D12(PE15)pin configuration */ gpio_af_set(GPIOE, GPIO_AF_12, EXMC_NBL0_PIN | EXMC_NBL1_PIN | EXMC_D4_PIN | EXMC_D5_PIN| EXMC_D8_PIN | EXMC_D9_PIN | EXMC_D10_PIN| EXMC_D11_PIN| EXMC_D12_PIN); gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_NBL0_PIN | EXMC_NBL1_PIN | EXMC_D4_PIN | EXMC_D5_PIN| EXMC_D8_PIN | EXMC_D9_PIN | EXMC_D10_PIN| EXMC_D11_PIN| EXMC_D12_PIN); gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_NBL0_PIN | EXMC_NBL1_PIN | EXMC_D4_PIN | EXMC_D5_PIN| EXMC_D8_PIN | EXMC_D9_PIN | EXMC_D10_PIN| EXMC_D11_PIN| EXMC_D12_PIN); //-----------------------PORT-F--------------------------------------------------// /* A0(PF0),A1(PF1),A2(PF2),A3(PF3),A4(PF4),A5(PF5),NRAS(PF11),A6(PF12),A7(PF13),A8(PF14),A9(PF15) pin configuration */ gpio_af_set(GPIOF, GPIO_AF_12, EXMC_A0_PIN | EXMC_A1_PIN | EXMC_A2_PIN | EXMC_A3_PIN |EXMC_A4_PIN|EXMC_A5_PIN | EXMC_SDNRAS_PIN | EXMC_A6_PIN | EXMC_A7_PIN| EXMC_A8_PIN | EXMC_A9_PIN); gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_A0_PIN | EXMC_A1_PIN | EXMC_A2_PIN | EXMC_A3_PIN |EXMC_A4_PIN|EXMC_A5_PIN | EXMC_SDNRAS_PIN | EXMC_A6_PIN | EXMC_A7_PIN| EXMC_A8_PIN | EXMC_A9_PIN); gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_A0_PIN | EXMC_A1_PIN | EXMC_A2_PIN | EXMC_A3_PIN |EXMC_A4_PIN| EXMC_A5_PIN | EXMC_SDNRAS_PIN | EXMC_A6_PIN | EXMC_A7_PIN| EXMC_A8_PIN | EXMC_A9_PIN); //-----------------------PORT-G--------------------------------------------------// /*A10(PG0),A11(PG1),A12(PG2),BA0/A14(PG4),BA1/A15(PG5), SDCLK(PG8), NCAS(PG15) pin configuration */ gpio_af_set(GPIOG, GPIO_AF_12, EXMC_A10_PIN | EXMC_A11_PIN | EXMC_A12_PIN| EXMC_BA0_PIN | EXMC_BA1_PIN | EXMC_SDCLK_PIN | EXMC_SDNCAS_PIN); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, EXMC_A10_PIN | EXMC_A11_PIN | EXMC_A12_PIN| EXMC_BA0_PIN | EXMC_BA1_PIN | EXMC_SDCLK_PIN | EXMC_SDNCAS_PIN); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, EXMC_A10_PIN | EXMC_A11_PIN | EXMC_A12_PIN| EXMC_BA0_PIN | EXMC_BA1_PIN | EXMC_SDCLK_PIN | EXMC_SDNCAS_PIN); /*specify which SDRAM to read and write*/ if(EXMC_SDRAM_DEVICE0 == sdram_device) { bank_select = EXMC_SDRAM_DEVICE0_SELECT; } else { bank_select = EXMC_SDRAM_DEVICE1_SELECT; } /* EXMC SDRAM device initialization sequence --------------------------------*/ /* step 1 : configure SDRAM timing registers --------------------------------*/ //150MHZ 主频,周期6.7ns. 下面内容参数配置根据SDRAM用户手册中的说明。 /* LMRD: 2 clock cycles */ sdram_timing_init_struct.load_mode_register_delay = 2; /*exit_selfrefresh=66ns*/ //XSRD sdram_timing_init_struct.exit_selfrefresh_delay = 11; /* ARFD: min=60ns */ sdram_timing_init_struct.auto_refresh_delay = 11; //自动刷新 /* RASD*/ sdram_timing_init_struct.row_address_select_delay = 11; /* WRD: min=2 Clock cycles */ sdram_timing_init_struct.write_recovery_delay = 3; /* RPD: min=12ns */ sdram_timing_init_struct.row_precharge_delay = 3; /* RCD: min=15ns */ sdram_timing_init_struct.row_to_column_delay = 3; /*step 2 : configure SDRAM control registers ---------------------------------*/ sdram_init_struct.sdram_device = sdram_device; /*看SDRAM手册:12行8列.*/ sdram_init_struct.column_address_width = EXMC_SDRAM_COW_ADDRESS_8; //列 sdram_init_struct.row_address_width = EXMC_SDRAM_ROW_ADDRESS_12; //行 sdram_init_struct.data_width =EXMC_SDRAM_DATABUS_WIDTH_16B; /*SDRAM内部bank数量*/ sdram_init_struct.internal_bank_number = EXMC_SDRAM_4_INTER_BANK; /*CAS潜伏期 */ sdram_init_struct.cas_latency = EXMC_CAS_LATENCY_3_SDCLK; /*禁止写保护*/ sdram_init_struct.write_protection = DISABLE; /*时钟分频因子,对于300MHZ进行的分频*/ sdram_init_struct.sdclock_config = EXMC_SDCLK_PERIODS_2_CK_EXMC; //时钟2分频=150MHZ.//MCU 手册中EXMC 时钟分频因子有几种选择。 /*突发读模式设置*/ sdram_init_struct.burst_read_switch = ENABLE; /*读延迟配置 */ sdram_init_struct.pipeline_read_delay = EXMC_PIPELINE_DELAY_1_CK_EXMC; sdram_init_struct.timing = &sdram_timing_init_struct; /*EXMC SDRAM bank initialization */ exmc_sdram_init(&sdram_init_struct); /*step3 : configure CKE high com nd---------------------------------------*/ sdram_com nd_init_struct.com nd = EXMC_SDRAM_CLOCK_ENABLE; sdram_com nd_init_struct.bank_select = bank_select; sdram_com nd_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_com nd_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the com nd */ exmc_sdram_com nd_config(&sdram_com nd_init_struct); /* step 4 : insert 10ms delay------------------------------------------------*/ _delay(100); /* step 5 : configure precharge all com nd----------------------------------*/ sdram_com nd_init_struct.com nd = EXMC_SDRAM_PRECHARGE_ALL; sdram_com nd_init_struct.bank_select = bank_select; sdram_com nd_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_com nd_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready检查SDRAM标志,等待至SDRAM空闲 */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the com nd */ exmc_sdram_com nd_config(&sdram_com nd_init_struct); /* step 6 : configure Auto-Refresh com nd-----------------------------------*/ sdram_com nd_init_struct.com nd = EXMC_SDRAM_AUTO_REFRESH; sdram_com nd_init_struct.bank_select = bank_select; // sdram_com nd_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_8_SDCLK; //原始 sdram_com nd_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_4_SDCLK; //2024-9-23 修改 for test.:也正常. sdram_com nd_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; /* 检查SDRAM标志,等待至SDRAM空闲 */ while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the com nd */ //发送第1次自动刷新命令. exmc_sdram_com nd_config(&sdram_com nd_init_struct); /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; /* 检查SDRAM标志,等待至SDRAM空闲 */ while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the com nd */ //发送第2次自动刷新命令. exmc_sdram_com nd_config(&sdram_com nd_init_struct); /* step 7 : configure load mode register com nd-----------------------------*/ /* program mode register */ com nd_content = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2| SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | SDRAM_MODEREG_CAS_LATENCY_3 | SDRAM_MODEREG_OPERATING_MODE_STANDARD | SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; sdram_com nd_init_struct.com nd = EXMC_SDRAM_LOAD_MODE_REGISTER; sdram_com nd_init_struct.bank_select = bank_select; sdram_com nd_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_com nd_init_struct.mode_register_content = com nd_content; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the com nd */ exmc_sdram_com nd_config(&sdram_com nd_init_struct); /* step 8 : set the auto-refresh rate counter--------------------------------*/ /* ms, 4096cle refresh, ms/4096=15.625us /* SDCLK_Freq = SYS_Freq/2 */ // GD32H759 的SDRAM 从300MHZ分过来,系数最小2,时钟最快150MHZ. /* (15.625us*SDCLK_Freq) - 20 */ // 最大15.625us*150M-20=2323. exmc_sdram_refresh_count_set(2323); /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } exmc_sdram_write_protection_config(sdram_device, DISABLE); }

以上:GD32H759 扩展的IS42S 00J-5BLI是驱动成功了,编写测试小程序:从SDRAM 读写8bit和16 bit 数据到SRAM, 看内存都是正确的。 接着,就可以把SDRAM 当成片上内存一样使用了。

<4> 调试 难题 拓展资料记录 | 开发 经过,遇到两次 难题:

硬件LDQM 和UDQM 两线设计反了,影响我程序中8bit 数据读写错误,上文已述; 这款SDRAM 手册中说明最高200MHZ, 实际我驱动程序按照200MHZ 配置,且验证:从SDRAM 读写8bit和16 bit 数据到SRAM, 看内存都是正确的。 然而:用到产品中(处理算法显示图像)就比较杂乱了,150MHZ 则是正常的,到目前没明确 缘故??? 以上,简单记录。 后期若200MHZ 图像 难题有进展,更新。 三. 其他      去年,还基于FMC 总线同时驱动过SDRAM 和 一款外部ADC 模拟芯片,数据采样也是正常的。 要注意:硬件原理图设计时,片选的连线。