STM32 H7系列学习笔记

必备的API知识

第 1 步:系统上电复位,进入启动文件 startup_stm32h743xx.s,在这个文件里面执行复位中断服务程序。

  • 在复位中断服务程序里面执行函数 SystemInit,在system_stm32h7xx.c 里面。*
  • 之后是调用编译器封装好的函数,比如用于 MDK 的启动文件是调__main,最终进入到 main函数*

第 2 步:进入到 main 函数就可以开始用户应用程序编程了。在这个函数里面要做几个重要的初始化,依次是:

  • MPU 初始化,需要用到库文件
    stm32h7xx_hal_cortex.c 和stm32h7xx_hal_cortex.h。
  • Cache 初始化,需要用到 core_cm7.h 文件。
  • HAL 库初始化函数 HAL_Init,需要用到文件 stm32h7xx_hal.c。
  • 系统时钟初始化,需要用到库文件 stm32h7xx_hal_rcc.c。

函数 HAL_Init()

HAL_StatusTypeDef HAL_Init(void)
{
/* 设置中断优先级分组 */HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);/* 使用滴答定时器做为默认时基,配置为 1ms 滴答,另外系统上电后默认使用的 HIS 时钟 */if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK){return HAL_ERROR;}/* 初始化底层硬件 */HAL_MspInit();/* 返回函数状态 */return HAL_OK;
}

此函数用于初始化 HAL 库,此函数主要实现如下功能:

  • 设置 NVIC 优先级分组是 4。
  • 设置滴答定时器的每 1ms 中断一次。
  • HAL 库不像之前的标准库,在系统启动函数 SystemInit 里面做了 RCC 初始化,HAL 库是没有做的,
    所以进入到 main 函数后,系统还在用内部高速时钟 HSI,对于 H7 来说,HSI 主频是 64MHz。
  • 函数 HAL_Init 里面调用的 HAL_MspInit 一般在文件 stm32h7xx_hal_msp.c 里面做具体实现,主要
    用于底层初始化。当前此函数也在文件 stm32h7xx_hal.c 里面,只是做了弱定义。

源文件 sttm32h7xx_hal_rcc.c

这个文件主要是实现内部和外部时钟(HSE、HSI、LSE、CSI、LSI、HSI48、PLL、CSS、MCO)以
及总线时钟(SYSCLK、AHB3、 AHB1、AHB2、AHB4、APB3、APB1L、APB1H、APB2、 APB4)的配置。
系统上电复位后,用户需要完成以下工作:
⚫选择用于驱动系统时钟的时钟源。
⚫ 配置系统时钟频率和 Flash 设置。
⚫ 配置分频器。
⚫ 使能外设时钟。
⚫ 配置外设时钟源,部分外设的时钟可以不来自系统时钟,此时通过配置寄存器 RCC_D1CCIPR、
RCC_D2CCIP1R、RCC_D2CCIP2R 和 RCC_D3CCIPR 实现

函数 HALRCCClockConfig()

RCC_ClkInitTypeDef RCC_ClkInitStruct;
HAL_StatusTypeDef ret = HAL_OK;/* 选择 PLL 的输出作为系统时钟配置 RCC_CLOCKTYPE_SYSCLK 系统时钟配置 RCC_CLOCKTYPE_HCLK 时钟,对应 AHB1,AHB2,AHB3 和 AHB4 总线配置 RCC_CLOCKTYPE_PCLK1 时钟,对应 APB1 总线配置 RCC_CLOCKTYPE_PCLK2 时钟,对应 APB2 总线配置 RCC_CLOCKTYPE_D1PCLK1 时钟,对应 APB3 总线配置 RCC_CLOCKTYPE_D3PCLK1 时钟,对应 APB4 总线 */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 |
RCC_CLOCKTYPE_PCLK1 | \
RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; 
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 
/* 此函数会更新 SystemCoreClock,并重新配置 HAL_InitTick */
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
if(ret != HAL_OK)
{Error_Handler(__FILE__, __LINE__);
}

源文件 sttm32h7xx_hal_cortex.c

这个库文件主要功能是 NVIC,MPU 和 Systick 的配置。此文件有个臃肿的地方,里面的 API 其实就
是将 ARM 的 CMSIS 库各种 API 重新封装了一遍。这么做的好处是保证了 HAL 的 API 都是以字母 HAL
开头。

MPU 单元

MPU 可以将 memory map 内存映射区分为多个具有一定访问规则的区域,通过这些规则可以实现
如下功能:
◆ 防止不受信任的应用程序访问受保护的内存区域。
◆ 防止用户应用程序破坏操作系统使用的数据。
◆ 通过阻止任务访问其它任务的数据区。
◆ 允许将内存区域定义为只读,以便保护重要数据。
◆ 检测意外的内存访问。
简单的说就是内存保护、外设保护和代码访问保护

MPU 可以配置保护 16 个内存区域(这 16 个内存域是独立配置的),每个区域最小要求 256 字节,
每个区域还可以配置为 8 个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是 32 字节,正好跟 Cache 的 Cache Line 大小一样。
MPU 可以配置的 16 个内存区的序号范围是 0 到 15,还有默认区 default region,也叫作背景区,序号-1。由于这些内存区可以嵌套和重叠,所以这些区域在嵌套或者重叠的时候有个优先级的问题。序号15 的优先级最高,以此递减,序号-1,即背景区的优先级最低。这些优先级是固定的。下面通过一个具体的实例帮助大家理解。如下所示共有 7 个区,背景区和序号 0-5 的区。内存区 4 跟内存区 0 和 1 有重叠部分,那么重叠部分将按照内存区 4 的配置规则执行;内存区 5 被完全包含在内存区3 里面,那么这部分内存区将按照内存区 5 的配置规则执行。

函数 HAL_MPU_ConfigRegion()

== 函数原型:==

void HAL_MPU_ConfigRegion(MPU_Region_InitTypeDef *MPU_Init)
{/* 部分省略未写 *//* Set the Region number */MPU->RNR = MPU_Init->Number;if ((MPU_Init->Enable) != RESET){/* 部分省略未写 */MPU->RBAR = MPU_Init->BaseAddress;MPU->RASR = ((uint32_t)MPU_Init->DisableExec << MPU_RASR_XN_Pos) |((uint32_t)MPU_Init->AccessPermission << MPU_RASR_AP_Pos) |((uint32_t)MPU_Init->TypeExtField << MPU_RASR_TEX_Pos) |((uint32_t)MPU_Init->IsShareable << MPU_RASR_S_Pos) |((uint32_t)MPU_Init->IsCacheable << MPU_RASR_C_Pos) |((uint32_t)MPU_Init->IsBufferable << MPU_RASR_B_Pos) |((uint32_t)MPU_Init->SubRegionDisable << MPU_RASR_SRD_Pos) |((uint32_t)MPU_Init->Size << MPU_RASR_SIZE_Pos) |((uint32_t)MPU_Init->Enable << MPU_RASR_ENABLE_Pos);}else{MPU->RBAR = 0x00;MPU->RASR = 0x00;}
}

函数参数
此函数的形参是一个 MPU_Region_InitTypeDef 类型的结构体变量,定义如下:

typedef struct
{uint8_t Enable; uint8_t Number; uint32_t BaseAddress; uint8_t Size; uint8_t SubRegionDisable; uint8_t TypeExtField; uint8_t AccessPermission; uint8_t DisableExec; uint8_t IsShareable; uint8_t IsCacheable; uint8_t IsBufferable; 
}MPU_Region_InitTypeDef;

STM32H7 的 Cache

当前芯片厂商出的 M7 内核芯片基本都做了一级 Cache 支持,Cache 又分数据缓存 D-Cache 和指令
缓冲 I-Cache,STM32H7 的数据缓存和指令缓存大小都是 16KB。对于指令缓冲,用户不用管,这里主
要说的是数据缓存 D-Cache。以 STM32H7 为例,主频是 400MHz,除了 TCM 和 Cache 以 400MHz
工作,其它 AXI SRAM,SRAM1,SRAM2 等都是以 200MHz 工作。数据缓存 D-Cache 就是解决 CPU
加速访问 SRAM。
在这里插入图片描述

如果每次 CPU 要读写 SRAM 区的数据,都能够在 Cache 里面进行,自然是最好的,实现了 200MHz
到 400MHz 的飞跃,实际是做不到的,因为数据 Cache 只有 16KB 大小,总有用完的时候。
对于使能了 Cache 的 SRAM 区,要分读写两种情况考虑。

读操作:

如果 CPU 要读取的 SRAM 区数据在 Cache 中已经加载好,这就叫读命中(Cache hit),如果 Cache
里面没有怎么办,这就是所谓的读 Cache Miss。

写操作:

如果 CPU 要写的 SRAM 区数据在 Cache 中已经开辟了对应的区域(专业词汇叫 Cache Line,以 32
字节为单位),这就叫写命中(Cache hit),如果 Cache 里面没有开辟对应的区域怎么办,这就是所谓的写 Cache Miss。

总结:

  1. Cortex-M7 内核的 L1 Cache 由多行内存区组成,每行有 32 字节,每行都配有一个地址标签。数据
    缓冲 DCache 是每 4 行为一组,称为 4-way set associative。而指令缓冲区 ICache 是 2 行为一组,
    这样节省地址标签,不用每个行都标记一个地址。
  2. 对于读操作,只有在第 1 次访问指定地址时才会加载到 Cache,而写操作的话,可以直接写到内存中
    (write-through 模式)或者放到 Cache 里面,后面再写入(write-back 模式)。
  3. 如果采用的是 Write back,Cache line 会被标为 dirty,等到此行被 evicted 时,才会执行实际的写
    操作,将 Cache Line 里面的数据写入到相应的存储区。
  4. Cache 命中是访问的地址落在了给定的 Cache Line 里面,所以硬件需要做少量的地址比较工作,以
    检查此地址是否被缓存。如果命中了,将用于缓存读操作或者写操作。如果没有命中,则分配和标记
    新行,填充新的读写操作。如果所有行都分配完毕了,Cache 控制器将支持 eviction 操作。根据 Cache
    Line 替换算法,一行将被清除 Clean,无效化 Invalid 或者重新配置。数据缓存和指令缓存是采用的
    伪随机替换算法。
  5. Cache 支持的 4 种基本操作,使能,禁止,清空和无效化。Clean 清空操作是将 Cache Line 中标记
    为 dirty 的数据写入到内存里面,而无效化 Invalid 是将 Cache Line 标记为无效,即删除操作。

面对繁冗复杂的 Cache 配置,推荐方式和安全隐患解决办法

◆ 推荐使用 128KB 的 TCM 作为主 RAM 区,其它的专门用于大缓冲和 DMA 操作等。
◆ Cache 问题主要是 CPU 和 DMA 都操作这个缓冲区时容易出现,使用时要注意。
◆ Cache 配置的选择,优先考虑的是 WB,然后是 WT 和关闭 Cache,其中 WB 和 WT 的使用中可以
配合 ARM 提供的函数解决上面说到的隐患问题(见本章 24.6 小节)。但不是万能的,在不起作用的
时候,直接暴力选择函数 SCB_CleanInvlaidateDCache 解决。关于这个问题,在分别配置以太网
MAC 的描述符缓冲区,发送缓冲区和接收缓冲区时尤其突出。

Cache 的相关函数

CMSIS 软件包的 core_cm7.h 文件为 Cache 的配置提供了 11 个函数:
◆ SCB_EnableICache
◆ SCB_DisableICache
◆ SCB_InvalidateICache
◆ SCB_EnableDCache
◆ SCB_DisableDCache
◆ SCB_InvalidateDCache
◆ SCB_CleanDCache
◆ SCB_CleanInvalidateDCache
◆ SCB_InvalidateDCache_by_Addr
◆ SCB_CleanDCache_by_Addr
◆ SCB_CleanInvalidateDCache_by_Addr
其中前三个函数是指令 Cache,比较容易掌握。重点是后面几个数据 Cache 函数。由于函数 SCB_CleanInvalidateDCache,SCB_CleanDCache 和 SCB_InvalidateDCache是对整个 Cache 的操作,所以比最后的三个函数 SCB_InvalidateDCache_by_Addr,SCB_CleanDCache_by_Addr 和 SCB_CleanInvalidateDCache_by_Addr 要耗时,当然,如果用户操作的存储器超过了数据 Cache 的大小,即 16KB,那么就跟前三个函数没有区别了。

STM32H7 的 TCM,SRAM 等五块内存基础知识

◆ TCM : Tightly-Coupled Memory 紧密耦合内存 。ITCM 用于指令,DTCM 用于数据,特点是跟内
核速度一样,而片上 RAM 的速度基本都达不到这个速度。
在这里插入图片描述
◆ ITCM 和 DTCM
这两个是直连 CPU 的。
◆ D1 Domain
D1 域中的各个外设是挂在 64 位 AXI 总线组成 67 的矩阵上。
⚫ 6 个从接口端 ASIB1 到 ASIB6
外接的主控是 LTDC,DMA2D,MDMA,SDMMC1,AXIM 和 D2-to-D1 AHB 总线。
⚫ 7 个主接口端 AMIB1 到 AMIB7
外接的从设备是 AHB3 总线,Flash A,Flash B,FMC 总线,QSPI 和 AXI SRAM。另外 AHB3
也是由 AXI 总线分支出来的,然后再由 AHB3 分支出 APB3 总线。
◆ D2 Domain
D2 域的各个外设是挂在 32 位 AHB 总线组成 10
9 的矩阵上。
⚫ 10 个从接口
外接的主控是 D1-to-D2 AHB 总线,AHBP 总线,DMA1,DMA2,Ethernet MAC,SDMMC2,
USB HS1 和 USB HS2。
⚫ 9 个主接口
外接的从设备是 SRAM1,SRMA2,SRAM3,AHB1,AHB2,APB1,APB2,D2-to-D1 AHB
总线和 D2-to-D3 AHB 总线。
◆ D3 Domain
D3 域的各个外设是挂在 32 位 AHB 总线组成 3*2 的矩阵上。
⚫ 3 个从接口
外接的主控 D1-to-D3 AHB 总线,D2-to-D3 AHB 总线和 BDMA。
⚫ 2 个主接口
外接的从设备是 AHB4,SRAM4 和 Bckp SRAM。另外 AHB4 也是这个总线矩阵分支出来的,
然后再由 AHB4 分支出 APB4 总线

各块 RAM 特性

TCM 区

TCM : Tightly-Coupled Memory 紧密耦合内存 。ITCM 用于运行指令,也就是程序代码,DTCM
用于数据存取,特点是跟内核速度一样,而片上 RAM 的速度基本都达不到这个速度,所以有降频处
理。
速度:400MHz。
DTCM 地址:0x2000 0000,大小 128KB。
ITCM 地址:0x0000 0000,大小 64KB。

AXI SRAM 区

位于 D1 域,数据带宽是 64bit,挂在 AXI 总线上。除了 D3 域中的 BDMB 主控不能访问,其它都可
以访问此 RAM 区。
速度:200MHz。
地址:0x2400 0000,大小 512KB。
用途:用途不限,可以用于用户应用数据存储或者 LCD 显存。

SRAM1,SRAM2 和 SRAM3 区

位于 D2 域,数据带宽是 32bit,挂在AHB 总线上。除了 D3域中的 BDMB主控不能访问这三块SRAM,
其它都可以访问这几个 RAM 区。
速度:200MHz。
SRAM1:地址 0x3000 0000,大小 128KB,用途不限,可用于 D2 域中的 DMA 缓冲,也可以当
D1 域断电后用于运行程序代码。
SRAM2:地址 0x3002 0000,大小 128KB,用途不限,可用于 D2 域中的 DMA 缓冲,也可以用于
用户数据存取。
SRAM3:地址 0x3004 0000,大小 32KB,用途不限,主要用于以太网和 USB 的缓冲。

SRAM4 区

位于 D3 域,数据带宽是 32bit,挂在 AHB 总线上,大部分主控都能访这块 SRAM 区。
速度:200MHz。
地址:0x3800 0000,大小 64KB。
用途:用途不限,可以用于 D3 域中的 DMA 缓冲,也可以当 D1 和 D2 域进入 DStandby 待机方式
后,继续保存用户数据。

Backup SRAM 区

备份 RAM 区,位于 D3 域,数据带宽是 32bit,挂在 AHB 总线上,大部分主控都能访问这块 SRAM
区。
速度:200MHz。
地址:0x3880 0000,大小 4KB。
用途:用途不限,主要用于系统进入低功耗模式后,继续保存数据(Vbat 引脚外接电池)。

串口的 HAL 库用法

串口的 HAL 库用法其实就是几个结构体变量成员的配置和使用,然后配置 GPIO、时钟,并根据需要
配置 NVIC、中断和 DMA。
HAL 库在 USART_TypeDef 的基础上封装了一个结构体 UART_HandleTypeDef,定义如下:

typedef struct
{USART_TypeDef *Instance; /*!< UART registers base address */UART_InitTypeDef Init; /*!< UART communication parameters */UART_AdvFeatureInitTypeDef AdvancedInit; /*!< UART Advanced Features initialization parameters */uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */uint16_t TxXferSize; /*!< UART Tx Transfer size */__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */uint16_t RxXferSize; /*!< UART Rx Transfer size */__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */uint16_t Mask; /*!< UART Rx RDR register mask */DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */HAL_LockTypeDef Lock; /*!< Locking object */__IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle managementand also related to Tx operations.This parameter can be a value of @ref HAL_UART_StateTypeDef */__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.This parameter can be a value of @ref HAL_UART_StateTypeDef */__IO uint32_t ErrorCode; /*!< UART Error code */
}UART_HandleTypeDef;

UART_InitTypeDef 结构体的定义如下:

typedef struct
{uint32_t BaudRate; /* 波特率 */uint32_t WordLength; /* 数据位长度 */uint32_t StopBits; /* 停止位 */ uint32_t Parity; /* 奇偶校验位 */ uint32_t Mode; /* 发送模式和接收模式使能 */ uint32_t HwFlowCtl; /* 硬件流控制 */ uint32_t OverSampling; /* 过采样,可以选择 8 倍和 16 倍过采样 */ uint32_t Prescaler; /* 串口分频 */ uint32_t FIFOMode; /* 串口 FIFO 使能 */ uint32_t TXFIFOThreshold; /* 发送 FIFO 的阀值 */ uint32_t RXFIFOThreshold; /* 接收 FIFO 的阀值 */ 
}UART_InitTypeDef;

UART_AdvFeatureInitTypeDef AdvancedInit(这个参数用于配置串口的高级特性)具体支持的功能参数如下:

typedef struct
{uint32_t AdvFeatureInit; /* 初始化的高级特性类别 */uint32_t TxPinLevelInvert; /* Tx 引脚电平翻转 */uint32_t RxPinLevelInvert; /* Rx 引脚电平翻转 */uint32_t DataInvert; /* 数据逻辑电平翻转 */uint32_t Swap; /* Tx 和 Rx 引脚交换 */uint32_t OverrunDisable; /* 接收超时检测禁止 */uint32_t DMADisableonRxError; /* 接收出错,禁止 DMA */uint32_t AutoBaudRateEnable; /* 自适应波特率使能 */uint32_t AutoBaudRateMode; /* 自适应波特率的四种检测模式选择 */uint32_t MSBFirst; /* 发送或者接收数据时,高位在前 */
} UART_AdvFeatureInitTypeDef;

示例

配置串口参数,其实就是配置结构体 UART_HandleTypeDef 的成员。比如下面配置为波特率 115200,8个数据位,无奇偶校验,1 个停止位。

UART_HandleTypeDef UartHandle;
/* USART3 工作在 UART 模式 */
/* 配置如下:- 数据位 = 8 Bits- 停止位 = 1 bit- 奇偶校验位 = 无- 波特率 = 115200bsp- 硬件流控制 (RTS 和 CTS 信号) */
UartHandle.Instance = USART3;
UartHandle.Init.BaudRate = 115200;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if(HAL_UART_Init(&UartHandle) != HAL_OK)
{
Error_Handler();
}

串口外设的基本参数配置完毕后还不能使用,还需要配置 GPIO、时钟、中断等参数,比如下面配置
串口 1,使用引脚 PA9 和 PA10。

/* 串口 1 的 GPIO PA9, PA10 */
#define USART1_CLK_ENABLE()			 __HAL_RCC_USART1_CLK_ENABLE()
#define USART1_TX_GPIO_CLK_ENABLE()	 __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART1_TX_GPIO_PORT 	GPIOA
#define USART1_TX_PIN 			GPIO_PIN_9
#define USART1_TX_AF 			GPIO_AF7_USART1
/*
*********************************************************************************************************
* 函 数 名: InitHardUart
* 功能说明: 配置串口的硬件参数和底层
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void InitHardUart(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit;
#if UART1_FIFO_EN == 1 /* 串口 1 */
/* 使能 GPIO TX/RX 时钟 */
USART1_TX_GPIO_CLK_ENABLE();
USART1_RX_GPIO_CLK_ENABLE();
/* 使能 USARTx 时钟 */
USART1_CLK_ENABLE();
/* 配置 TX 引脚 */
GPIO_InitStruct.Pin = USART1_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = USART1_TX_AF;
HAL_GPIO_Init(USART1_TX_GPIO_PORT, &GPIO_InitStruct);
/* 配置 RX 引脚 */
GPIO_InitStruct.Pin = USART1_RX_PIN;
GPIO_InitStruct.Alternate = USART1_RX_AF;
HAL_GPIO_Init(USART1_RX_GPIO_PORT, &GPIO_InitStruct);
/* 配置 NVIC the NVIC for UART */ 
HAL_NVIC_SetPriority(USART1_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(USART1_IRQn);/* 配置波特率、奇偶校验 */
bsp_SetUartParam(USART1, UART1_BAUD, UART_PARITY_NONE, UART_MODE_TX_RX);
SET_BIT(USART1->ICR, USART_ICR_TCCF); /* 清除 TC 发送完成标志 */
SET_BIT(USART1->RQR, USART_RQR_RXFRQ); /* 清除 RXNE 接收标志 */
SET_BIT(USART1->CR1, USART_CR1_RXNEIE);/* 使能 PE. RX 接受中断 */
#endif
}

串口的状态标志清除问题

__HAL_USART_GET_FLAG 函数。这个函数用来检查 USART 标志位是否被设置。

/** @brief Check whether the specified USART flag is set or not.* @param __HANDLE__: specifies the USART Handle* @param __FLAG__: specifies the flag to check.* This parameter can be one of the following values:* @arg USART_FLAG_TXFT: TXFIFO threshold flag* @arg USART_FLAG_RXFT: RXFIFO threshold flag* @arg USART_FLAG_RXFF: RXFIFO Full flag* @arg USART_FLAG_TXFE: TXFIFO Empty flag* @arg USART_FLAG_REACK: Receive enable ackowledge flag
* @arg USART_FLAG_TEACK: Transmit enable ackowledge flag* @arg USART_FLAG_BUSY: Busy flag* @arg USART_FLAG_TXE: Transmit data register empty flag* @arg USART_FLAG_TC: Transmission Complete flag* @arg USART_FLAG_RXNE: Receive data register not empty flag* @arg USART_FLAG_IDLE: Idle Line detection flag* @arg USART_FLAG_ORE: OverRun Error flag* @arg USART_FLAG_UDR: UnderRun Error flag* @arg USART_FLAG_NE: Noise Error flag* @arg USART_FLAG_FE: Framing Error flag* @arg USART_FLAG_PE: Parity Error flag* @retval The new state of __FLAG__ (TRUE or FALSE).*/
#define __HAL_USART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->ISR & (__FLAG__)) == (__FLAG__))

USART 标志是需要软件主动清零的。清零有两种方式:一种是调用__HAL_USART_CLEAR_FLAG 函数,另一种是操作相关寄存器后自动清零。

/** @brief Clear the specified USART pending flag.* @param __HANDLE__: specifies the USART Handle.* @param __FLAG__: specifies the flag to check.* This parameter can be any combination of the following values:* @arg USART_FLAG_TXFT: TXFIFO threshold flag* @arg USART_FLAG_RXFT: RXFIFO threshold flag* @arg USART_FLAG_RXFF: RXFIFO Full flag* @arg USART_FLAG_TXFE: TXFIFO Empty flag* @arg USART_FLAG_REACK: Receive enable ackowledge flag* @arg USART_FLAG_TEACK: Transmit enable ackowledge flag* @arg USART_FLAG_WUF: Wake up from stop mode flag* @arg USART_FLAG_RWU: Receiver wake up flag (is the USART in mute mode)* @arg USART_FLAG_SBKF: Send Break flag* @arg USART_FLAG_CMF: Character match flag* @arg USART_FLAG_BUSY: Busy flag* @arg USART_FLAG_ABRF: Auto Baud rate detection flag* @arg USART_FLAG_ABRE: Auto Baud rate detection error flag* @arg USART_FLAG_RTOF: Receiver timeout flag* @arg USART_FLAG_LBD: LIN Break detection flag* @arg USART_FLAG_TXE: Transmit data register empty flag* @arg USART_FLAG_TC: Transmission Complete flag* * @arg USART_FLAG_RXNE: Receive data register not empty flag* @arg USART_FLAG_IDLE: Idle Line detection flag* @arg USART_FLAG_ORE: OverRun Error flag* @arg USART_FLAG_NE: Noise Error flag* @arg USART_FLAG_FE: Framing Error flag* @arg USART_FLAG_PE: Parity Error flag* @retval The new state of __FLAG__ (TRUE or FALSE).*/
#define __HAL_USART_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->ICR = (__FLAG__))

RS485 的基础知识

关于 RS485 的逻辑状态,不同厂家的芯片的定义可能不同,但不影响正常的数据收发,这里以 TI 的
为例做个说明,TI 的定义方式如下:
A 表示非反向输出 non-inverting output,B 表示反向输出 inverting output。
当 VA > VB 的时候表示逻辑状态 0,被称为 ON。
当 VA < VB 的时候表示逻辑状态 1,被称为 OFF。
在这里插入图片描述
对应到实际芯片框图上就是下面这样(DE 发送使能,D 是发送数据端,RE 是接收使能,R 是接收数据端)
在这里插入图片描述
当用户在 D(Driver)引脚输入逻辑高电平时,将在 485 总线上实现逻辑状态 0,即 ON 状态。接收端 R
(Receiver)将收到逻辑高电平。
当用户在 D(Driver)引脚输入逻辑低电平时,将在 485 总线上实现逻辑状态 1,即 OFF 状态。接收端 R
(Receiver)将收到逻辑低电平。

FMC 基础知识

FMC 的几个关键知识点放在开头说:
◆ STM32H7 的 FMC 总线是挂载 64 位带宽的 AXI 总线上,F1,F4 和 F7 是挂在 32 位总线上。
◆ 使用 FMC,可以用来外挂 NOR/PSRAM 型存储器,SRAM 型存储器,NAND 型存储器,SDRAM
存储器等,从而可以用来驱动 AD7606,OLED,DM9000 等并行控制设备。
◆ 支持 8 位,16 位和 32 位总线带宽控制。
◆ 每个片选下的存储器空间配置都是独立的,有专门的寄存器,互不影响。

FMC 时钟选择

在这里插入图片描述

FMC地址区域

FMC 总线可操作的地址范围 0x60000000 到 0xDFFFFFFF。
在这里插入图片描述
与 F1 和 F4 不同,H7 系列的 FMC 总线接口支持重映射,也就是可以设置这几块存储器的位置。
在这里插入图片描述
◆ 对于 NOR/PSRAM/SRAM 块区。
这个块区用到的地方最多,像 NAND 和 SDRAM 块区基本只能接 NAND 和 SDRAM,而
NOR/PSRAM/SRAM 区就不同了,除了能接这几种类型的存储器,还可以外接 DM9000,SDRAM,OLED,AD7606 等总线外设。这个块区有 4 路片选,分别是 FMC_NE1,FMC_NE2,FMC_NE3 和 FMC_NE4,这几个片选在芯片上都有对应的引脚,每个片选可以管理 64MB 的访问空间,这个是由 FMC 引出的 26路地址线 FMC_A[0:25]决定的,2^26 = 64MB。
⚫ FMC_NE1:首地址 0x6000 0000,可以管理的地址范围 0x6000 0000 到 0x63FF FFFF。
⚫ FMC_NE2:首地址 0x6400 0000,可以管理的地址范围 0x6400 0000 到 0x67FF FFFF。
⚫ FMC_NE3:首地址 0x6800 0000,可以管理的地址范围 0x6800 0000 到 0x6BFF FFFF。
⚫ FMC_NE4:首地址 0x6C00 0000,可以管理的地址范围 0x6C00 0000 到 0x6FFF FFFF。

NOR/PSRAM/SRAM 时序控制

F103 和 F407 仅支持 16 位总线访问,等到 F429,H7 已经支持 32 位总线访问。以驱动 SRAM 为例,
需要用到下面的数据,地址和控制引脚。配置完毕后,就可以像使用内部 SRAM 一样进行读写了,使用
比较方便。
在这里插入图片描述

NOR/PSARM/SRAM 时序配置结构体

FMC_NORSRAM_TimingTypeDef

typedef struct
{uint32_t AddressSetupTime; //此参数用于设置地址建立时间,单位 FMC 时钟周期个数,范围 0 -15。同步 NOR Flash 用不到此参
数。uint32_t AddressHoldTime; //此参数用于设置地址持续时间,单位 FMC 时钟周期个数,范围 1 -15。同步 NOR Flash 用不到此参
数。uint32_t DataSetupTime; //此参数用于设置数据建立时间,单位 FMC 时钟周期个数,范围 1 -255。用于 SRAM,异步多路复用uint32_t BusTurnAroundDuration; //此参数用于设置总线 TurnAround(总线周转阶段)持续时间,单位 FMC 时钟周期个数,范围 0 -15。仅用于多路复用 NOR Flash。uint32_t CLKDivision; //此参数用于设置时钟分频,范围 2 -16,仅用于同步器件。uint32_t DataLatency; //对于使能了读/写突发模式的同步访问,此参数定义了读写首个数据前要发送给存储器的时钟周期个数。//操作 CRAM,此参数必须为 0。//异步 NOR/PSRAM/SRAM 器件用不到此参数。//使能了同步突发模式的 NOR Flash,此参数的范围是 2 – 17,单位 FMC 时钟周期个数。 uint32_t AccessMode; //用于设置 FMC 的访问模式
}FMC_NORSRAM_TimingTypeDef;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/807048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Socket 通信机制详解

Socket 是网络编程中一种重要的通信机制&#xff0c;它允许不同的计算机通过网络进行数据交换。 一、 Socket 的概念 Socket&#xff08;套接字&#xff09;是计算机网络编程中的一种抽象&#xff0c;它提供了在网络上进行通信的接口。 Socket 本质上是一种通信的端点&#…

Vue3 ts环境下的PropType

简介 在Typscript中&#xff0c;我们可以使用PropType进行类型的推断与验证。在日常的开发中我们常常会遇到下面这样的场景&#xff1a; 我们通过request请求从服务端获取了一条数据&#xff0c;数据是个Array的格式&#xff0c;Array中的每个元素又是一个对象&#xff0c;像下…

坚持十天做完Python入门编程100题第三天加班

坚持十天做完Python入门编程100题第三天加班 第24题 扫描文件列表第25题 如何将字典转换成JSON并写入json文件&#xff1f;第26题 JSON转换成字典 第24题 扫描文件列表 如何扫描当前目录下的文件列表&#xff1f;解析&#xff1a;可以使用python内置的glob模块&#xff0c;用法…

Golang | Leetcode Golang题解之第21题合并两个有序链表

题目&#xff1a; 题解&#xff1a; func mergeTwoLists(list1, list2 *ListNode) *ListNode {if list1 nil {return list2 // 注&#xff1a;如果都为空则返回空}if list2 nil {return list1}if list1.Val < list2.Val {list1.Next mergeTwoLists(list1.Next, list2)re…

项目管理工具——使用甘特图制定项目计划的详细步骤

甘特图是一种直观的项目管理工具&#xff0c;它有助于我们清晰地展示任务安排、时间管理和项目的进度。以下是使用甘特图制定项目计划的详细步骤&#xff1a; 1、创建项目&#xff1a;首先&#xff0c;在进度猫中创建新的项目&#xff0c;并设置项目的时间、工作日等参数。根据…

44-技术演进(下):软件架构和应用生命周期技术演进之路

应用、系统资源、应用生命周期管理这 3 个维度&#xff0c;构成了我们对云的所有诉求。 我会介绍下应用维度和应用生命周期管理维度的技术演进。 我们就先来看下软件架构的演进之路。 软件架构的演进 软件架构技术演进如下图所示&#xff1a; 单体架构 在单体架构中&#xff…

乐得瑞LDR6020 Type-C 一拖二/一拖三快充线方案介绍

随着移动设备的普及和功能的日益增强&#xff0c;电池续航成为了用户关注的重点之一。为了满足用户对于快速充电的需求&#xff0c;各大厂商纷纷推出了各种快充技术和产品。在这个背景下&#xff0c;乐得瑞公司推出了一款名为LDR6020的一分二PD快充线方案&#xff0c;该方案采用…

Unity 九宫格

1. 把图片拖拽进资源文件夹 2.选中图片&#xff0c;然后设置图片 3.设置九宫格 4.使用图片&#xff0c;在界面上创建2个相同的Image,然后使用图片&#xff0c;修改Image Type 为Sliced

Nodejs 第六十二章(短链接)

短链接介绍 短链接是一种缩短长网址的方法&#xff0c;将原始的长网址转换为更短的形式。它通常由一系列的字母、数字和特殊字符组成&#xff0c;比起原始的长网址&#xff0c;短链接更加简洁、易于记忆和分享。 短链接的主要用途之一是在社交媒体平台进行链接分享。由于这些…

配置及第三方授权申请教程

项目需要配置的地方不多&#xff0c;主要就两个地方需要注意&#xff1a;邮箱授权和第三方授权需要提前申请 1.基本设置 1.1 打开application.yml&#xff0c;修改数据库ip等基本信息 这些基本的配置就不多说了&#xff0c;基本就是改下服务器ip和账号密码什么的 1.2 获取QQ…

RX4901CE自带SPI接口,适合用在需高精度和快速响应的设备

传统的模拟温度补偿晶振采用热敏电阻等元器件来检测环境温度&#xff0c;将温度信息做相应变换后控制晶振的输出频率用来实现稳定输出&#xff0c;但是这种做法频率补偿精度有限。伴随目前电路计算频率越来越高&#xff0c;更多工业级的高时间精度和快速时间响应的应用出现&…

MLeaksFinder报错

1.报错&#xff1a;FBClassStrongLayout.mm 文件&#xff1a;layoutCache[currentClass] ivars; 解决&#xff1a;替换为layoutCache[(id)currentClass] ivars; 2.编译正常但运行时出现crash indirect_symbol_bindings[i] cur->rebinding FBRetainCycleDetector iOS15 …

Mybatis分页查询用PageHelper插件

首先看接口文档需求 看响应数据样例&#xff0c;那么咱们先自定义一个bean来满足这个需求&#xff0c;这里定义PageBean实体类 package com.itheima.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.util.List;//分…

Ubuntu-22.04安装Virtualbox并安装Windows10

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Virtualbox是什么&#xff1f;二、安装Virtualbox1.关闭Secure Boot2.安装 三、安装Windows101.新装虚拟机基本配置2.新装虚拟机核心配置 总结 前言 虚拟机…

Harmony鸿蒙南向驱动开发-UART

UART指异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;是通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输。 两个UART设备的连接示意图如下&#xff0c;UART与其他模块一般用2线&a…

记录一个Kafka客户端Offset Explore连不上的问题

CSDN上的其他人不知道咋想的&#xff0c;这么简单一个问题都要写个收费的回答。那我来写个不收费的&#xff0c; 我昨天把集群重装了一下&#xff0c;再连这个工具就连不上了&#xff08;你先把zk和kafka在集群启起来&#xff09;&#xff0c;报错截图如下&#xff1a; 英文翻…

中位数和众数-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第49讲。 中位数和众数&…

深入浅出Redis(十二):Redis的排序命令Sort

引言 Redis是一款快速、优秀的键值对数据库&#xff0c;提供丰富的数据结构能在各种场景下实现功能&#xff0c;同时也提供丰富的命令来完成各种各样的功能&#xff0c;本篇文章将深入浅出的解析Sort命令的原理以及使用 原理 Sort 命令用来对list、set、zset对象进行排序&am…

JavaEE实验三:3.5学生信息查询系统(动态Sql)

题目要求: 使用动态SQL进行条件查询、更新以及复杂查询操作。本实验要求利用本章所学知识完成一个学生信息系统&#xff0c;该系统要求实现3个以下功能: 1、多条件查询&#xff1a; 当用户输入的学生姓名不为空&#xff0c;则根据学生姓名进行学生信息的查询&#xff1b; 当用户…

Day:006(1) | Python爬虫:高效数据抓取的编程技术(爬虫工具)

selenium介绍与安装 Selenium是一个Web的自动化测试工具&#xff0c;最初是为网站自动化测试而开发的&#xff0c;类型像我们玩游戏用的按键精灵&#xff0c;可以按指定的命令自动操作&#xff0c;不同是Selenium 可以直接运行在浏览器上&#xff0c;它支持所有主流的浏览器&am…