2025 Make Blocks阶段2 - 基于STM32H743的MCU核心板设计
该项目使用了stm32h742iit6,实现了核心板的设计,它的主要功能为:驱动板载的sdram和flash,点亮rgb屏幕。
标签
Flash
STM32H7
sdram
悠悠兹有
更新2025-08-20
27
KiCad文件
全屏

一 项目介绍

本项目主要使用了芯片stm32h743,主要使用了视频控制器,qspi控制器和fmc控制器。控制板载芯片和实现视频的输出。

选择的任务是用于简易示波器/频谱仪/信号发生器的核心板。核心板引出来所有的引脚,板载了主要使用的外设,sdram主要用于图像显示。

nanflash用于保存数据。

引出rgb接口,用于连接外部rgb显示屏幕。

引出了usb接口。

二 模块硬件和功能介绍

STM32H743II: 意法半导体(STMicroelectronics)推出的​​高性能嵌入式处理器​​,基于 Arm Cortex-M7 内核,主频高达 ​​480 MHz​。处理器架构搭载 ​​Cortex-M7 内核​​,支持双精度浮点单元(FPU)和全套 DSP 指令,擅长实时信号处理、电机控制等数学密集型任务。其 ​​1027 DMIPS​​ 的运算能力(Dhrystone 2.1基准)远超同类产品,可流畅运行图形界面或多任务系统。存储配置​​,​​2 MB 双区 Flash​​:支持读写同步,适合存储大型固件与数据。1 MB RAM​​:包含 ​​192 KB 超高速 TCM RAM​​(64 KB指令+128 KB数据),专为实时任务优化;另有 864 KB 用户 SRAM,满足动态数据缓存需求。集成了​​35+个通信接口​​与​​11类模拟外设​​,覆盖绝大多数工业与消费电子需求。芯片划分为 ​​3 个独立电源域​​(D1高性能核心/D2通信/D3控制),可单独关闭未用模块。​​待机功耗仅 2.95 µA​​(关闭备份 SRAM,保留 RTC),适合电池供电设备(如便携医疗仪器)。当你的项目需要同时驱动触摸屏、处理传感器网络、控制电机并联网交互时,它就是那块能扛起所有任务的“瑞士军刀级芯片”。故采用此芯片,学习rgb屏幕的驱动,和nandflash的驱动。存储和显示一直就是本人想深入学习的知识点。


nandflash:W25N01GVZEIG​​ 是 Winbond(华邦电子)推出的 ​​1Gb(128MB) SPI NAND 闪存芯片​​,采用 ​​WSON-8 封装​​。作为工业级高性能存储器,它通过 SPI 接口简化设计,适用于对空间、功耗和数据可靠性要求较高的场景。支持 ​​104MHz 时钟频率​​,快速响应数据请求。典型擦写次数 ​​10 万次​​(需配合均衡算法延长寿命)


sdram:W9825G6KH-6​​ 芯片容量 ​​256Mbit​​(32MB,组织结构为 ​​16M × 16位​​)。最高 ​​166MHz​​(CL=3),支持突发读写操作(突发长度可配置为1、2、4、8或整页)CAS延迟(CL)为3个时钟周期,行预充时间(tRP)≤15ns,行列地址延迟(tRCD)≤15ns每 ​​64ms​​ 需刷新8192行,刷新间隔约 ​​7.81μs​。属于Winbond 的经典 ​​256Mb SDRAM​​,以 ​​166MHz 高频、低功耗及工业级可靠性​​ 成为消费电子与嵌入式系统的性价比之选。设计时需严格遵循时序约束和PCB规范,高EMC环境建议增加π型滤波电路。若遇供应紧张,可考虑工业级型号或低频替代品,但需评估容量与性能是否匹配需求。

三 设计思路

板载芯片较少,主要是一些对时许要求高的存储,显示等外设。其余扩展版在引出。

当前重点调试得芯片是 nandflash usb 和sdram。

image.png

缺陷:没有讲通用的接口如iic spi等接口单独引出做一个独立接口引出,后续会通过扩展板引出所有需要的接口。

四 原理图/pcb介绍

1 介绍

16针得usb插口,带外一个电源指示灯和3.3v转换电路。

image.png

引出所有得空闲引脚,方便扩展版得使用。

image.png

h7外围电路,有一个可编程得led 。

image.png

下面是sdram得外围电路。

image.png

nandflash得电路,使用了qspi接口。

image.png

下面是pcb的设计图。

image.png

2 优化设计

在调试过程中,第一版没引出的引脚有点多,有点遗憾。测试的过程中,虽然板载外设使用问题不大,可是板子看起来有点丑,玩起来不开心,于是更新了原理图,将nandflash,rgb引线和sdram做了等长,重新打板,焊接后板子如下图。

image.png

外设驱动展示

项目导出cmake编译,使用linux环境做开发,项目配置如下

image.png

image.png

时钟配置,按照上限配置时钟参数

image.png

配置用户灯引脚,ph7

image.png

控制一下led,看一下板子是否可控

  while (1)
  {
    /* USER CODE END WHILE */


    /* USER CODE BEGIN 3 */
        HAL_Delay(1000);
        HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_RESET);
        HAL_Delay(1000);
        HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_SET);


  }


1 串口的驱动

开启uart的输出,用于打印日志,调试程序

cube配置如下,开启uart1,查看原理图的uart引脚,配置相应的引脚

image.png

因为是使用gnuc,增加函数复写代码,如下

#ifdef __GNUC__
int _write(int fd, char *ptr, int len)  
{  
  HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF);
  return len;
}
#endif

就可以愉快的使用printf写日志了

    printf("\r\n\r\n****************************************************************\r\n");
    printf("****************************************************************\r\n");
    printf("-------------hello man!!------tm[[%s]-------\r\n",__TIMESTAMP__);
    printf("****************************************************************\r\n");

效果如下

image.png


2 nandflash的驱动

下面进行对板载的flash进行读写

首先cube配置qspi控制器

image.png

w25n01gvzeig flash 设备id

image.png

对比了一下全功能qspi flash,w25n01gvzeig并不是完整的qspi设备,仅仅支持读写的qspi命令。


image.png

image.png

因为指令较少,需要的初始化流程较少,所以初始化完控制器后,仅需少量的初始化代码就能进行读写操作。

重启设备:#define  QSPI_CMD_RESET                     0xff

开启写:#define  QSPI_CMD_W_EN                      0x06

读出jedec id

     QSPI_CommandTypeDef s_command;
     uint32_t Temp = 0;
     uint8_t pData[4];
     /* 读取JEDEC ID */
     s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
     s_command.Instruction       = QSPI_CMD_JEDEC_ID;
     s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
     s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
     s_command.DataMode          = QSPI_DATA_1_LINE;
     s_command.AddressMode       = QSPI_ADDRESS_NONE;
     s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
     s_command.DummyCycles       = 0;
     s_command.NbData            = 4;
     s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
     s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
     s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;


     if(HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
         Log_info_G("something wrong ....\r\n");


     }
     if(HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {
         Log_info_G("something wrong ....\r\n");
     }


     Temp = ( pData[2] | pData[1]<<8 )| ( pData[0]<<16 );
    Log_info_G("*****************pData[%x:%x:%x:%x]*************\r\n",pData[0],pData[1],pData[2],pData[3]);


     return Temp;

效果:

image.png


擦除一个块

// 擦除一个 block
uint8_t BSP_QSPI_erase_block(uint8_t pageAd)
{
    W25NXX_Write_enable();
    QSPI_CommandTypeDef s_command;
    uint8_t pData[4] = {0x00, 0x00, 0x00, 0x00};


    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = QSPI_CMD_BLOCK_ERASE;


    s_command.AddressMode       = QSPI_ADDRESS_NONE;
    s_command.AddressSize       = QSPI_ADDRESS_8_BITS;
    s_command.Address            = 0xB0; // 读取起始地址


    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    s_command.AlternateBytes = 0;
    s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS;


    s_command.DummyCycles       = 8;


    s_command.DataMode          = QSPI_DATA_1_LINE;
    s_command.NbData            = 2;


    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;


     if(HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
         Log_info_G(" cmd wrong ....\r\n");
         return 1;
     }
     pData[0] = (pageAd >> 8) & 0xFF; // 高字节
     pData[1] = pageAd & 0xFF; // 低字节
     if(HAL_QSPI_Transmit(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {
         Log_info_G(" data wrong ....\r\n");
     }
    Log_info_G("erase block addr: [%d]~ ....\r\n",pageAd);
    wait_flash_ready_tmout();
    return QSPI_OK;
}

对特定页写入数据

 uint32_t QSPI_FLASH_write_page(uint32_t pageAd,uint8_t* dataPtr,uint32_t wrByte)
 {
     QSPI_CommandTypeDef s_command;
     uint32_t Temp = 0;
     uint8_t pData[4]= {01,0};


    W25NXX_Write_enable(); // 发送写使能命令
    BSP_QSPI_erase_block(pageAd);


     // 等待擦除完成
    wait_flash_ready_tmout();


    W25NXX_Write_enable(); // 发送写使能命令


    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = QSPI_CMD_WR_ALL_BUF;


    s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
    s_command.AddressSize       = QSPI_ADDRESS_16_BITS;
    s_command.Address            = 2; // 读取起始地址


    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    s_command.AlternateBytes    = 0;
    s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_16_BITS;


    s_command.DummyCycles       = 0;


    s_command.DataMode          = QSPI_DATA_1_LINE;
    s_command.NbData            = wrByte;


    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;


     if(HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
         Log_info_G("QSPI_FLASH_write_page cmd wrong ....\r\n");
         return 1;
     }
     if(HAL_QSPI_Transmit(&hqspi, dataPtr, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {
         Log_info_G("QSPI_FLASH_write_page data wrong ....\r\n");
     }
      Log_info_G("QSPI_FLASH_write_page success~ ..wrByte[%d]..\r\n",wrByte);


    // 等待写入完成
    wait_flash_ready_tmout();
    Log_info_G("QSPI_FLASH_write_page end~ ....\r\n");


    // write load
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = QSPI_CMD_LOAD_EXEC;


    s_command.AddressMode       = QSPI_ADDRESS_NONE;
    s_command.AddressSize       = QSPI_ADDRESS_16_BITS;
    s_command.Address            = 2; // 读取起始地址


    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    s_command.AlternateBytes    = 0;
    s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_16_BITS;


    s_command.DummyCycles       = 8;


    s_command.DataMode          = QSPI_DATA_1_LINE;
    s_command.NbData            = 2;


    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
     if(HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
         Log_info_G("QSPI_FLASH_write_page cmd wrong ....\r\n");
         return 1;
     }
     pData[0] = (pageAd >> 8) & 0xFF; // 高字节
     pData[1] = pageAd & 0xFF;
     if(HAL_QSPI_Transmit(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {
         Log_info_G("QSPI_FLASH_write_page data wrong ....\r\n");
     }


     return Temp;
 }

读特定页

uint8_t BSP_QSPI_Read(uint32_t pageAd,uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
{
    QSPI_FLASH_load_page(pageAd);
     QSPI_CommandTypeDef s_command;
     /* 初始化读命令 */
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = QSPI_CMD_READ;


    s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
    s_command.AddressSize       = QSPI_ADDRESS_16_BITS;
    s_command.Address            = ReadAddr; // 读取起始地址


    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    s_command.AlternateBytes    = ReadAddr;
    s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_16_BITS;


    s_command.DummyCycles       = 8;


    s_command.DataMode          = QSPI_DATA_1_LINE;
    s_command.NbData            = Size;


    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;


     /* 配置命令 */
    if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){
        Log_info_Q("send cmd fail!!");
         return QSPI_ERROR;
     }else{
        Log_info_Q("read :send cmd success!!");
     }


     /* 接收数据 */
     if(HAL_QSPI_Receive(&hqspi, pData,HAL_QPSI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {
        Log_info_Q("read data fail!!");
         return QSPI_ERROR;
     }
     return QSPI_OK;
}

下面一个测试例子,再99页,第二个字节开始写入字符串:

    char *dataPtr = "Hello, QSPI Flash! by zsy";

测试代码:

image.png

测试效果如下

image.png

3 移植letter-shell

克隆代码地址:NevermindZZT/letter-shell: letter shell

简单使用的话,移植比较简单,实现一个uart的读写函数即可。

对于裸机程序,把读的实现放到中断里。

主要代码如下

初始化函数

void User_Shell_Init(void)
{
    //注册自己实现的写函数
    shell.write = userShellWrite;
 
    //调用shell初始化函数
    shellInit(&shell, shell_buffer, 512);
}

写函数

short userShellWrite(char *data, unsigned short len)
{
    HAL_UART_Transmit(&huart1, data, len, 0xFFFF);
    return 0;
}

读函数

extern Shell shell;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    /* 判断是哪个串口触发的中断 */
    if(huart ->Instance == USART1)
    {
        HAL_UART_Receive_IT(&huart1, (uint8_t*)&hrecv_buf, 1);
        shellHandler(&shell, hrecv_buf);
    }
}

创建一个新命令

void helloWorld(void)
{
    int a;
    char b[12];
    shellPrint(shellGetCurrent(), "hello world!!\r\n");
}
SHELL_EXPORT_CMD(
SHELL_CMD_PERMISSION(0x00)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
hw_out, helloWorld, my test);

效果如下

image.png

输入:hw_out,打印hello world。

image.png


问题:

1 使用ansi转义打印颜色,通过minicom发现日志不能显示颜色

原因:终端(如linux控制台)支持标准ANSI转义序列色彩。由于termcap显然没有对于色彩的支持,因而minicom 硬性内置了这些转义序列的代码。所以此选项缺省为off。使用‘-c on’可以打开此项

使用开启minicom时候,加上 -c on即可

image.png

4 usb的驱动和cherryusb的移植

下载源码:cherry-embedded/CherryUSB: CherryUSB is a tiny and beautiful, high performace and portable USB host and device stack for embedded system with USB IP

stm芯片demo代码:https://github.com/CherryUSB/cherryusb_stm32.git

在项目里新建一个文件夹,把cherryusb复制进去

使用cubemx帮助配置usb 并初始化usb控制器。

修改cmakelist,这里也就是所有的工作量了,具体应用使用源码里的实例。修改如下:

image.png

增加两行代码:

    extern void msc_ram_init(uint8_t busid, uint32_t reg_base);
    msc_ram_init(0, USB_OTG_FS_PERIPH_BASE);

此处使用源码提供的msc例程,方便快捷的开启usb应用。

效果如本文的视频所示。空间大小512k字节,代码使用的ram空间作为u盘的实际空间断电u盘数据重置。

六 代码

库地址:https://gitee.com/zhang-sy/make-blocks_1_stm32h743ii

七 结语

stm32h743外设有点多,硬件框架有点复杂,引脚也多,目前还是在熟悉外设的过程中。一直想学习一些复杂一点的芯片。h7芯片除了没mmu,也和正经的soc没什么区别了。但是soc相比,开发困难度低个维度,所以将来会持续多学习一一段时间,相信对于本职工作会有很大裨益。


软硬件
电路图
附件下载
h7.zip
文件压缩后还是太大,删除了cmsis等官方依赖文件
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号