当前位置: 首页 > news >正文

51c嵌入式~单片机~合集6

我自己的原文哦~    https://blog.51cto.com/whaosoft/13127816

一、STM32单片机的知识点总结 

本文将以STM32F10x为例,对标准库开发进行概览。主要分为三块内容:

  • STM32系统结构
  • 寄存器
  • 通过点灯案例,详解如何基于标准库构建STM32工程 
STM32系统结构

    上图,STM32f10xxx系统结构。

内核IP

    从结构框图上看,Cortex-M3内部有若干个总线接口,以使CM3能同时取址和访内(访问内存),它们是:指令存储区总线(两条)、系统总线、私有外设总线。有两条代码存储区总线负责对代码存储区(即 FLASH 外设)的访问,分别是 I-Code 总线和 D-Code 总线。

    I-Code用于取指,D-Code用于查表等操作,它们按最佳执行速度进行优化。

    系统总线(System)用于访问内存和外设,覆盖的区域包括SRAM,片上外设,片外RAM,片外扩展设备,以及系统级存储区的部分空间。

    私有外设总线负责一部分私有外设的访问,主要就是访问调试组件。它们也在系统级存储区。

    还有一个DMA总线,从字面上看,DMA是data memory access的意思,是一种连接内核和外设的桥梁,它可以访问外设、内存,传输不受CPU的控制,并且是双向通信。简而言之,这个家伙就是一个速度很快的且不受老大控制的数据搬运工。

处理器外设(内核之外的外设)

    从结构框图上看,STM32的外设有串口、定时器、IO口、FSMC、SDIO、SPI、I2C等,这些外设按照速度的不同,分别挂载到AHB、APB2、APB1这三条总线上。

寄存器

    什么是寄存器?寄存器是内置于各个IP外设中,是一种用于配置外设功能的存储器,并且有想对应的地址。一切库的封装始于映射。

    是不是看的眼都花了,如果进行寄存器开发,就需要怼地址以及对寄存器进行字节赋值,不仅效率低而且容易出错。

    库的存在就是为了解决这类问题,将代码语义化。语义化思想不仅仅是嵌入式有的,前端代码也在追求语义特性。

从点灯开始学习STM32
内核库文件分析

cor_cm3.h

    这个头文件实现了:

  • 内核结构体寄存器定义。 
  • 内核寄存器内存映射。 
  • 内存寄存器位定义。跟处理器相关的头文件stm32f10x.h实现的功能一样,一个是针对内核的寄存器,一个是针对内核之外,即处理器的寄存器。

misc.h

    内核应用函数库头文件,对应stm32f10x_xxx.h。

misc.c

    内核应用函数库文件,对应stm32f10x_xxx.c。在CM3这个内核里面还有一些功能组件,如NVIC、SCB、ITM、MPU、CoreDebug,CM3带有非常丰富的功能组件,但是芯片厂商在设计MCU的时候有一些并不是非要不可的,是可裁剪的,比如MPU、ITM等在STM32里面就没有。

    其中NVIC在每一个CM3内核的单片机中都会有,但都会被裁剪,只能是CM3 NVIC的一个子集。在NVIC里面还有一个SysTick,是一个系统定时器,可以提供时基,一般为操作系统定时器所用。misc.h和mics.c这两个文件提供了操作这些组件的函数,并可以在CM3内核单片机直接移植。

处理器外设库文件分析

startup_stm32f10x_hd.s

    这个是由汇编编写的启动文件,是STM32上电启动的第一个程序,启动文件主要实现了

  • 初始化堆栈指针 SP;
  • 设置 PC 指针=Reset_Handler ;
  • 设置向量表的地址,并 初始化向量表,向量表里面放的是 STM32 所有中断函数的入口地址
  • 调用库函数 SystemInit,把系统时钟配置成 72M,SystemInit 在库文件 stytem_stm32f10x.c 中定义;
  • 跳转到标号_main,最终去到 C 的世界。

system_stm32f10x.c

    这个文件的作用是里面实现了各种常用的系统时钟设置函数,有72M,56M,48, 36,24,8M,我们使用的是是把系统时钟设置成72M。

Stm32f10x.h

    这个头文件非常重要,这个头文件实现了:

  • 处理器外设寄存器的结构体定义。
  • 处理器外设的内存映射。
  • 处理器外设寄存器的位定义。

    关于 1 和 2 我们在用寄存器点亮 LED 的时候有讲解。

    其中 3:处理器外设寄存器的位定义,这个非常重要,具体是什么意思?

    我们知道一个寄存器有很多个位,每个位写 1 或者写 0 的功能都是不一样的,处理器外设寄存器的位定义就是把外设的每个寄存器的每一个位写 1 的 16 进制数定义成一个宏,宏名即用该位的名称表示,如果我们操作寄存器要开启某一个功能的话,就不用自己亲自去算这个值是多少,可以直接到这个头文件里面找。相关文章:C语言操作寄存器的常见手法。

    我们以片上外设 ADC 为例,假设我们要启动 ADC 开始转换,根据手册我们知道是要控制 ADC_CR2 寄存器的位 0:ADON,即往位 0 写 1,即:

ADC->CR2=0x00000001;

    这是一般的操作方法。现在这个头文件里面有关于 ADON 位的位定义:

#define ADC_CR2_ADON ((uint32_t)0x00000001)

有了这个位定义,我们刚刚的代码就变成了:

ADC->CR2=ADC_CR2_ADON

stm32f10x_xxx.h

    外设 xxx 应用函数库头文件,这里面主要定义了实现外设某一功能的结构体,比如通用定时器有很多功能,有定时功能,有输出比较功能,有输入捕捉功能,而通用定时器有非常多的寄存器要实现某一个功能。

    比如定时功能,我们根本不知道具体要操作哪些寄存器,这个头文件就为我们打包好了要实现某一个功能的寄存器,是以机构体的形式定义的,比如通用定时器要实现一个定时的功能,我们只需要初始化 TIM_TimeBaseInitTypeDef 这个结构体里面的成员即可,里面的成员就是定时所需要操作的寄存器。

    有了这个头文件,我们就知道要实现某个功能需要操作哪些寄存器,然后再回手册中精度这些寄存器的说明即可。

stm32f10x_xxx.c

    stm32f10x_xxx.c:外设 xxx 应用函数库,这里面写好了操作 xxx 外设的所有常用的函数,我们使用库编程的时候,使用的最多的就是这里的函数。

SystemInit

    工程中新建main.c 。

    在此文件中编写main函数后直接编译会报错:

Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o).

    错误提示说SystemInit没有定义。从分析启动文件startup_stm32f10x_hd.s时我们知道。

;Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
;IMPORT SystemInit
;LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

汇编中;分号是注释的意思

    第五行第六行代码Reset_Handler调用了SystemInit该函数用来初始化系统时钟,而该函数是在库文件system_stm32f10x.c中实现的。我们重新写一个这样的函数也可以,把功能完整实现一遍,但是为了简单起见,我们在main文件里面定义一个SystemInit空函数,为的是骗过编译器,把这个错误去掉。

    关于配置系统时钟之后会出文章RCC时钟树详细介绍,主要配置时钟控制寄存器(RCC_CR)和时钟配置寄存器(RCC_CFGR)这两个寄存器,但最好是直接使用CubeMX直接生成,因为它的配置过程有些冗长。

    如果我们用的是库,那么有个库函数SystemInit,会帮我们把系统时钟设置成72M。

    现在我们没有使用库,那现在时钟是多少?答案是8M,当外部HSE没有开启或者出现故障的时候,系统时钟由内部低速时钟LSI提供,现在我们是没有开启HSE,所以系统默认的时钟是LSI=8M。

库封装层级

    如图,达到第四层级便是我们所熟知的固件库或HAL库的效果。当然库的编写还需要考虑许多问题,不止于这些内容。我们需要的是了解库封装的大概过程。

    将库封装等级分为四级来介绍是为了有层次感,就像打怪升级一样,进行认知理解的升级。

    我们都知道,操作GPIO输出分三大步:

时钟控制:

    STM32 外设很多,为了降低功耗,每个外设都对应着一个时钟,在系统复位的时候这些时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。

    STM32 的所有外设的时钟由一个专门的外设来管理,叫RCC(reset and clockcontrol),RCC 在STM32 参考手册的第六章。

    STM32 的外设因为速率的不同,分别挂载到三条总系上:AHB、APB2、APB1,AHB为高速总线,APB2 次之,APB1 再次之。所以的IO 口都挂载到APB2 总线上,属于高速外设。

模式配置:

    这个由端口配置寄存器来控制。端口配置寄存器分为高低两个,每4bit 控制一个IO 口,所以端口配置低寄存器:CRL 控制这IO 口的低8 位,端口配置高寄存器:CRH控制这IO 口的高8bit。

    在4 位一组的控制位中,CNFy[1:0] 用来控制端口的输入输出,MODEy[1:0]用来控制输出模式的速率,又称驱动电路的响应速度,注意此处速率与程序无关,GPIO引脚速度、翻转速度、输出速度区别输入有4种模式,输出有4种模式,我们在控制LED 的时候选择通用推挽输出。

    输出速率有三种模式:2M、10M、50M,这里我们选择2M。

电平控制:

    STM32的IO口比较复杂,如果要输出1和0,则要通过控制:端口输出数据寄存器ODR来实现,ODR 是:Output data register的简写,在STM32里面,其寄存器的命名名称都是英文的简写,很容易记住。

    从手册上我们知道ODR是一个32位的寄存器,低16位有效,高16位保留。低16位对应着IO0~IO16,只要往相应的位置写入0或者1就可以输出低或者高电平。

    第一层级:基地址宏定义 

    时钟控制:

    在STM32中,每个外设都有一个起始地址,叫做外设基地址,外设的寄存器就以这个基地址为标准按照顺序排列,且每个寄存器32位,(后面作为结构体里面的成员正好内存对齐)。

查表看到时钟由APB2外设时钟使能寄存器(RCC_APB2ENR)来控制,其中PB端口的时钟由该寄存器的位3写1使能。我们可以通过基地址+偏移量0x18,算出RCC_APB2ENR的地址为:0x40021018。那么使能PB口的时钟代码则如下所示:

#define RCC_APB2ENR *(volatile unsigned long *)0x40021018// 开启端口B 时钟RCC_APB2ENR |= 1<<3;

    模式配置:

    同RCC_APB2ENR一样,GPIOB的起始地址是:0X4001 0C00,我们也可以算出GPIO_CRL的地址为:0x40010C00。那么设置PB0为通用推挽输出,输出速率为2M的代码则如下所示:

    同上,从手册中我们看到ODR寄存器的地址偏移是:0CH,可以算出GPIOB_ODR寄存器的地址是:0X4001 0C00 + 0X0C = 0X4001 0C0C。现在我们就可以定义GPIOB_ODR这个寄存器了,代码如下:

#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C//PB0 输出低电平
GPIOB_ODR = 0<<0;

    第一层级:基地址宏定义完成用STM32控制一个LED的完整代码:

#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR *(volatile unsigned long *)0x40010C0Cint main(void)
{// 开启端口B 的时钟RCC_APB2ENR |= 1<<3;// 配置PB0 为通用推挽输出模式,速率为2MGPIOB_CRL = (2<<0) | (0<<2);// PB0 输出低电平,点亮LEDGPIOB_ODR = 0<<0;
}void SystemInit(void)
{
}

    第二层级:基地址宏定义+结构体封装

外设寄存器结构体封装

    上面我们在操作寄存器的时候,操作的是寄存器的绝对地址,如果每个寄存器都这样操作,那将非常麻烦。我们考虑到外设寄存器的地址都是基于外设基地址的偏移地址,都是在外设基地址上逐个连续递增的,每个寄存器占32个或者16个字节,这种方式跟结构体里面的成员类似。

    所以我们可以定义一种外设结构体,结构体的地址等于外设的基地址,结构体的成员等于寄存器,成员的排列顺序跟寄存器的顺序一样。这样我们操作寄存器的时候就不用每次都找到绝对地址,只要知道外设的基地址就可以操作外设的全部寄存器,即操作结构体的成员即可。

    下面我们先定义一个GPIO寄存器结构体,结构体里面的成员是GPIO的寄存器,成员的顺序按照寄存器的偏移地址从低到高排列,成员类型跟寄存器类型一样。

typedef struct 
{volatile uint32_t CRL;volatile uint32_t CRH;volatile uint32_t IDR;volatile uint32_t ODR;volatile uint32_t BSRR;volatile uint32_t BRR;volatile uint32_t LCKR;
} GPIO_TypeDef;

    在《STM32 中文参考手册》8.2 寄存器描述章节,我们可以找到结构体里面的7个寄存器描述。在点亮LED的时候我们只用了CRL和ODR这两个寄存器,至于其他寄存器的功能大家可以自行看手册了解。

    在GPIO结构体里面我们用了两个数据类型,一个是uint32_t,表示无符号的32位整型,因为GPIO的寄存器都是32位的。这个类型声明在标准头文件stdint.h 里面使用typedef对unsigned int重命名,我们在程序上只要包含这个头文件即可。

    另外一个是volatile作用就是告诉编译器这里的变量会变化不因优化而省略此指令,必须每次都直接读写其值,这样就能确保每次读或者写寄存器都真正执行到位。

外设封装

    STM32F1系列的GPIO端口分A~G,即GPIOA、GPIOB。。。。。。GPIOG。每个端口都含有GPIO_TypeDef结构体里面的寄存器,我们可以根据手册各个端口的基地址把GPIO的各个端口定义成一个GPIO_TypeDef类型指针,然后我们就可以根据端口名(实际上现在是结构体指针了)来操作各个端口的寄存器,代码实现如下:

#define GPIOA ((GPIO_TypeDef *) 0X4001 0800)
#define GPIOB ((GPIO_TypeDef *) 0X4001 0C00)
#define GPIOC ((GPIO_TypeDef *) 0X4001 1000)
#define GPIOD ((GPIO_TypeDef *) 0X4001 1400)
#define GPIOE ((GPIO_TypeDef *) 0X4001 1800)
#define GPIOF ((GPIO_TypeDef *) 0X4001 1C00)
#define GPIOG ((GPIO_TypeDef *) 0X4001 2000)

外设内存映射

    讲到基地址的时候我们再引人一个知识点:Cortex-M3存储器系统,这个知识点在《Cortex-M3权威指南》第5章里面讲到。CM3的地址空间是4GB,如下图所示:

我们这里要讲的是片上外设,就是我们所说的寄存器的根据地,其大小总共有512MB,512MB是其极限空间,并不是每个单片机都用得完,实际上各个MCU厂商都只是用了一部分而已。STM32F1系列用到了:0x4000 0000 ~0x5003 FFFF。现在我们说的STM32的寄存器就是位于这个区域。

APB1、APB2、AHB 总线基地址

    现在我们说的STM32的寄存器就是位于这个区域,这里面ST设计了三条总线:AHB、APB2和APB1,其中AHB和APB2是高速总线,APB1是低速总线。不同的外设根据速度不同分别挂载到这三条总线上。

    从下往上依次是:APB1、APB2、AHB,每个总线对应的地址分别是:APB1:0x40000000,APB2:0x4001 0000,AHB:0x4001 8000。

    这三条总线的基地址我们是从《STM32 中文参考手册》2.3小节—存储器映像得到的:APB1的基地址是TIM2定时器的起始地址,APB2的基地址是AFIO的起始地址,AHB的基地址是SDIO的起始地址。其中APB1地址又叫做外设基地址,是所有外设的基地址,叫做PERIPH_BASE。

    现在我们把这三条总线地址用宏定义出来,以后我们在定义其他外设基地址的时候,只需要在这三条总线的基址上加上偏移地址即可,代码如下:

#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)

GPIO 端口基地址

    因为GPIO挂载到APB2总线上,那么现在我们就可以根据APB2的基址算出各个GPIO端口的基地址,用宏定义实现代码如下:

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)

    第二层级:基地址宏定义+结构体封装完成用STM32控制一个LED的完整代码:

#include <stdint.h>
#define __IO volatiletypedef struct 
{__IO uint32_t CRL;__IO uint32_t CRH;__IO uint32_t IDR;__IO uint32_t ODR;__IO uint32_t BSRR;__IO uint32_t BRR;__IO uint32_t LCKR;
} GPIO_TypeDef;typedef struct 
{__IO uint32_t CR;__IO uint32_t CFGR;__IO uint32_t CIR;__IO uint32_t APB2RSTR;__IO uint32_t APB1RSTR;__IO uint32_t AHBENR;__IO uint32_t APB2ENR;__IO uint32_t APB1ENR;__IO uint32_t BDCR;__IO uint32_t CSR;
} RCC_TypeDef;#define PERIPH_BASE ((uint32_t)0x40000000)#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define RCC ((RCC_TypeDef *) RCC_BASE)#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C

-------------------------------

int main(void)
{// 开启端口B 的时钟RCC->APB2ENR |= 1<<3;// 配置PB0 为通用推挽输出模式,速率为2MGPIOB->CRL = (2<<0) | (0<<2);// PB0 输出低电平,点亮LEDGPIOB->ODR = 0<<0;
}void SystemInit(void)
{
}

    第二层级变化:

    ①、定义一个外设(GPIO)寄存器结构体,结构体的成员包含该外设的所有寄存器,成员的排列顺序跟寄存器偏移地址一样,成员的数据类型跟寄存器的一样。

    ②外设内存映射,即把地址跟外设建立起一一对应的关系。

    ③外设声明,即把外设的名字定义成一个外设寄存器结构体类型的指针。

    ④通过结构体操作寄存器,实现点亮LED。

    第三层级:基地址宏定义+结构体封装+“位封装”(每一位的对应字节封装)

上面我们在控制GPIO输出内容的时候控制的是ODR(Output data register)寄存器,ODR是一个16位的寄存器,必须以字的形式控制其实我们还可以控制BSRR和BRR这两个寄存器来控制IO的电平,下面我们简单介绍下BRR寄存器的功能,BSRR自行看手册研究。

    位清除寄存器BRR只能实现位清0操作,是一个32位寄存器,低16位有效,写0没影响,写1清0。现在我们要使PB0输出低电平,点亮LED,则只要往BRR的BR0位写1即可,其他位为0,代码如下:

GPIOB->BRR = 0X0001;

    这时PB0就输出了低电平,LED就被点亮了。

    如果要PB2输出低电平,则是:

GPIOB->BRR = 0X0004;

    如果要PB3/4/5/6。。。。。。这些IO输出低电平呢?

    道理是一样的,只要往BRR的相应位置赋不同的值即可。因为BRR是一个16位的寄存器,位数比较多,赋值的时候容易出错,而且从赋值的16进制数字我们很难清楚的知道控制的是哪个IO。

    这时,我们是否可以把BRR的每个位置1都用宏定义来实现,如GPIO_Pin_0就表示0X0001,GPIO_Pin_2就表示0X0004。只要我们定义一次,以后都可以使用,而且还见名知意。“位封装”(每一位的对应字节封装) 代码如下:

#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */

    这时PB0就输出了低电平的代码就变成了:

GPIOB->BRR = GPIO_Pin_0;

    如果同时让PB0/PB15输出低电平,用或运算,代码:

GPIOB->BRR = GPIO_Pin_0|GPIO_Pin_15;

    为了不使main函数看起来冗余,上述库封装 的代码不应该放在main里面,因为其是跟GPIO相关的,我们可以把这些宏放在一个单独的头文件里面。

    在工程目录下新建stm32f10x_gpio.h,把封装代码放里面,然后把这个文件添加到工程里面。这时我们只需要在main.c里面包含这个头文件即可。

    第四层级:基地址宏定义+结构体封装+“位封装”+函数封装

    我们点亮LED的时候,控制的是PB0这个IO,如果LED接到的是其他IO,我们就需要把GPIOB修改成其他的端口,其实这样修改起来也很快很方便。

    但是为了提高程序的可读性和可移植性,我们是否可以编写一个专门的函数用来复位GPIO的某个位,这个函数有两个形参,一个是GPIOX(X=A...G),另外一个是GPIO_Pin(0...15),函数的主体则是根据形参GPIOX 和GPIO_Pin来控制BRR寄存器,代码如下:

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{GPIOx->BRR = GPIO_Pin;
}

    这时,PB0输出低电平,点亮LED的代码就变成了:

GPIO_ResetBits(GPIOB,GPIO_Pin_0);

    同理, 我们可以控制BSRR这个寄存器来实现关闭LED,代码如下:

// GPIO 端口置位函数
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{GPIOx->BSRR = GPIO_Pin;
}

    这时,PB0输出高电平,关闭LED的代码就变成了:

GPIO_SetBits(GPIOB,GPIO_Pin_0);

    同样,因为这个函数是控制GPIO的函数,我们可以新建一个专门的文件来放跟gpio有关的函数。

    在工程目录下新建stm32f10x_gpio.c,把GPIO相关的函数放里面。这时我们是否发现刚刚新建了一个头文件stm32f10x_gpio.h,这两个文件存放的都是跟外设GPIO相关的。

    C文件里面的函数会用到h头文件里面的定义,这两个文件是相辅相成的,故我们在stm32f10x_gpio.c 文件中也包含stm32f10x_gpio.h这个头文件。别忘了把stm32f10x.h这个头文件也包含进去,因为有关寄存器的所有定义都在这个头文件里面。

    如果我们写其他外设的函数,我们也应该跟GPIO一样,新建两个文件专门来存函数,比如RCC这个外设我们可以新建stm32f10x_rcc.c和stm32f10x_rcc.h。其他外依葫芦画瓢即可。

延庆川北小区45孙老师 收卖废品破烂垃圾炒股 废品孙

实例编写

    以上,是对库封住过程的概述,下面我们正在地使用库函数编写LED程序。

①管理库的头文件

    当我们开始调用库函数写代码的时候,有些库我们不需要,在编译的时候可以不编译,可以通过一个总的头文件stm32f10x_conf.h来控制,该头文件主要代码如下:

    这里面包含了全部外设的头文件,点亮一个LED我们只需要RCC和GPIO 这两个外设的库函数即可,其中RCC控制的是时钟,GPIO控制的具体的IO口。所以其他外设库函数的头文件我们注释掉,当我们需要的时候就把相应头文件的注释去掉即可。

    stm32f10x_conf.h这个头文件在stm32f10x.h这个头文件的最后面被包含,在第8296行:

#ifdef USE_STDPERIPH_DRIVER
#include "stm32f10x_conf.h"
#endif

    代码的意思是,如果定义了USE_STDPERIPH_DRIVER这个宏的话,就包含stm32f10x_conf.h这个头文件。

    我们在新建工程的时候,在魔术棒选项卡C/C++中,我们定义了USE_STDPERIPH_DRIVER 这个宏,所以stm32f10x_conf.h 这个头文件就被stm32f10x.h包含了,我们在写程序的时候只需要调用一个头文件:stm32f10x.h即可。

②编写LED初始化函数

    经过寄存器点亮LED的操作,我们知道操作一个GPIO输出的编程要点大概如下:

1、开启GPIO的端口时钟

2、选择要具体控制的IO口,即pin

3、选择IO口输出的速率,即speed

4、选择IO口输出的模式,即mode

5、输出高/低电平

    STM32的时钟功能非常丰富,配置灵活,为了降低功耗,每个外设的时钟都可以独自的关闭和开启。STM32中跟时钟有关的功能都由RCC这个外设控制,RCC中有三个寄存器控制着所以外设时钟的开启和关闭:RCC_APHENR、RCC_APB2ENR和RCC_APB1ENR,AHB、APB2和APB1代表着三条总线,所有的外设都是挂载到这三条总线上,GPIO属于高速的外设,挂载到APB2总线上,所以其时钟有RCC_APB2ENR控制。

GPIO 时钟控制

    固件库函数:RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE)函数的原型为:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,FunctionalState NewState)
{
/* Check the parameters */assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE) {RCC->APB2ENR |= RCC_APB2Periph;} 
else{RCC->APB2ENR &= ~RCC_APB2Periph;}
}

    当程序编译一次之后,把光标定位到函数/变量/宏定义处,按键盘的F12或鼠标右键的Go to definition of,就可以找到原型。固件库的底层操作的就是RCC外设的APB2ENR这个寄存器,宏RCC_APB2Periph_GPIOB的原型是:0x00000008,即(1<<3),还原成存器操作就是:RCC->APB2ENR |= 1<<<3。相比固件库操作,寄存器操作的代码可读性就很差,只有才查阅寄存器配置才知道具体代码的功能,而固件库操作恰好相反,见名知意。

GPIO 端口配置

    GPIO的pin,速度,模式,都由GPIO的端口配置寄存器来控制,其中IO0~IO7由端口配置低寄存器CRL控制,IO8~IO15由端口配置高寄存器CRH配置。固件库把端口配置的pin,速度和模式封装成一个结构体:

typedef struct
{
uint16_t GPIO_Pin;GPIOSpeed_TypeDef GPIO_Speed;GPIOMode_TypeDef GPIO_Mode;
} GPIO_InitTypeDef;

    pin可以是GPIO_Pin_0~GPIO_Pin_15或者是GPIO_Pin_All,这些都是库预先定义好的宏。speed也被封装成一个结构体:

typedef enum
{GPIO_Speed_10MHz = 1,GPIO_Speed_2MHz,GPIO_Speed_50MHz
} GPIOSpeed_TypeDef;

    速度可以是10M,2M或者50M,这个由端口配置寄存器的MODE位控制,速度是针对IO口输出的时候而言,在输入的时候可以不用设置。mode也被封装成一个结构体:

typedef enum
{GPIO_Mode_AIN = 0x0, // 模拟输入GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入(复位后的状态)GPIO_Mode_IPD = 0x28, // 下拉输入GPIO_Mode_IPU = 0x48, // 上拉输入GPIO_Mode_Out_OD = 0x14, // 通用开漏输出GPIO_Mode_Out_PP = 0x10, // 通用推挽输出GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
} GPIOMode_TypeDef;

    IO口的模式有8种,输入输出各4种,由端口配置寄存器的CNF配置。平时用的最多的就是通用推挽输出,可以输出高低电平,驱动能力大,一般用于接数字器件。

    最终用固件库实现就变成这样:

// 定义一个GPIO_InitTypeDef 类型的结构体
GPIO_InitTypeDef GPIO_InitStructure;// 选择要控制的IO 口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;// 设置引脚为推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;// 设置引脚速率为50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;/*调用库函数,初始化GPIOB0*/
GPIO_Init(GPIOB, &GPIO_InitStructure);

    倘若同一端口下不同引脚有不同的模式配置,每次对每个引脚配置完成后都要调用GPIO初始化函数,代码如下:

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ;                      
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;                     
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO 输出控制

    GPIO输出控制,可以通过端口数据输出寄存器ODR、端口位设置/清除寄存器BSRR和端口位清除寄存器BRR这三个来控制。端口输出寄存器ODR是一个32位的寄存器,低16位有效,对应着IO0~IO15,只能以字的形式操作,一般使用寄存器操作。

// PB0 输出高电平,点亮LED
GPIOB->ODR = 1<<0;

    端口位清除寄存器BRR是一个32位的寄存器,低十六位有效,对应着IO0~IO15,只能以字的形式操作,可以单独对某一个位操作,写1清0。

// PB0 输出低电平,点亮LED
GPIO_ResetBits(GPIOB, GPIO_Pin_0);

    BSRR是一个32位的寄存器,低16位用于置位,写1有效,高16位用于复位,写1有效,相当于BRR寄存器。高16位我们一般不用,而是操作BRR这个寄存器,所以BSRR这个寄存器一般用来置位操作。

// PB0 输出高电平,熄灭LED
GPIO_SetBits(GPIOB, GPIO_Pin_0);

    综上:固件库LED GPIO初始化函数。

void LED_GPIO_Config(void)
{
// 定义一个GPIO_InitTypeDef 类型的结构体GPIO_InitTypeDef GPIO_InitStructure;// 开启GPIOB 的时钟RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);// 选择要控制的IO 口GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;// 设置引脚为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;// 设置引脚速率为50MHzGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/*调用库函数,初始化GPIOB0*/GPIO_Init(GPIOB, &GPIO_InitStructure);// 关闭LEDGPIO_SetBits(GPIOB, GPIO_Pin_0);
}

主函数

#include "stm32f10x.h"void SOFT_Delay(__IO uint32_t nCount);
void LED_GPIO_Config(void);int main(void)
{
// 程序来到main 函数之前,启动文件:statup_stm32f10x_hd.s 已经调用
// SystemInit()函数把系统时钟初始化成72MHZ
// SystemInit()在system_stm32f10x.c 中定义
// 如果用户想修改系统时钟,可自行编写程序修改LED_GPIO_Config();while ( 1 ) {
// 点亮LEDGPIO_ResetBits(GPIOB, GPIO_Pin_0);Time_Delay(0x0FFFFF);// 熄灭LEDGPIO_SetBits(GPIOB, GPIO_Pin_0);Time_Delay(0x0FFFFF);}
}
// 简陋的软件延时函数
void Time_Delay(volatile uint32_t Count)
{
for (; Count != 0; Count--);
}

注意void Time_Delay(volatile uint32_t Count)只是一个简陋的软件延时函数,如果小伙伴们有兴趣可以看一看MultiTimer,它是一个软件定时器扩展模块,可无限扩展所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。

二、xxx
三、xxx

相关文章:

51c嵌入式~单片机~合集6

我自己的原文哦~ https://blog.51cto.com/whaosoft/13127816 一、STM32单片机的知识点总结 本文将以STM32F10x为例&#xff0c;对标准库开发进行概览。主要分为三块内容&#xff1a; STM32系统结构寄存器通过点灯案例&#xff0c;详解如何基于标准库构建STM32工程 STM3…...

SQL Server Management Studio 表内数据查询与删除指令

查询指令 //select * from 表名称 where 列名称 数据名称 select * from Card_Info where num CC3D4D删除指令&#xff0c;删除数据库有风险&#xff0c;操作不可逆&#xff0c;建议删除前备份&#xff0c;以免删错。 //Delete * from 表名称 where 列名称 数据名称 Delete f…...

Timesheet.js - 轻松打造炫酷时间表

Timesheet.js - 轻松打造炫酷时间表 前言 在现代网页设计中&#xff0c;时间表是一个常见的元素&#xff0c;用于展示项目进度、历史事件、个人经历等信息。 然而&#xff0c;创建一个既美观又功能强大的时间表并非易事。 幸运的是&#xff0c;Timesheet.js 这款神奇的 Java…...

产品经理面试题总结2025【其一】

一、产品理解与定位 1、你如何理解产品经理这个角色&#xff1f; 作为一名互联网产品经理&#xff0c;我理解这个角色的核心在于成为产品愿景的制定者和执行的推动者。具体来说&#xff0c;产品经理是连接市场、用户和技术团队之间的桥梁&#xff0c;负责理解市场需求、用户痛…...

第16章:Python TDD实现多币种货币运算

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…...

【Web】2025-SUCTF个人wp

目录 SU_blog SU_photogallery SU_POP SU_blog 先是注册功能覆盖admin账号 以admin身份登录&#xff0c;拿到读文件的权限 ./article?filearticles/..././..././..././..././..././..././etc/passwd ./article?filearticles/..././..././..././..././..././..././proc/1…...

C++实现矩阵Matrix类 实现基本运算

本系列文章致力于实现“手搓有限元&#xff0c;干翻Ansys的目标”&#xff0c;基本框架为前端显示使用QT实现交互&#xff0c;后端计算采用Visual Studio C。 目录 Matrix类 1、public function 1.1、构造函数与析构函数 1.2、获取矩阵数值 1.3、设置矩阵 1.4、矩阵转置…...

【GORM】初探gorm模型,字段标签与go案例

GORM是什么&#xff1f; GORM 是一个Go 语言 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它让我们可以使用结构体来操作数据库&#xff0c;而无需编写SQL 语句 GORM 模型与字段标签详解 在 GORM 中&#xff0c;模型是数据库表的抽象表示&#xff0c;字段标签&am…...

html全局遮罩,通过websocket来实现实时发布公告

1.index.html代码示例 <div id"websocket" style"display:none;position: absolute;color:red;background-color: black;width: 100%;height: 100%;z-index: 100; opacity: 0.9; padding-top: 30%;padding-left: 30%; padding-border:1px; "onclick&q…...

基于VSCode+CMake+debootstrap搭建Ubuntu交叉编译开发环境

基于VSCodeCMakedebootstrap搭建Ubuntu交叉编译开发环境 1 基于debootstrap搭建目标系统环境1.1 安装必要软件包1.2 创建sysroot目录1.3 运行debootstrap1.4 挂载必要的虚拟文件系统1.5 复制 QEMU 静态二进制文件1.6 进入目标系统1.7 使用目标系统&#xff08;以安装zlog为例&a…...

C#中System.Text.Json:从入门到精通的实用指南

一、引言 在当今数字化时代&#xff0c;数据的高效交换与处理成为软件开发的核心环节。JSON&#xff08;JavaScript Object Notation&#xff09;凭借其简洁、轻量且易于读写的特性&#xff0c;已然成为数据交换领域的中流砥柱。无论是前后端数据交互&#xff0c;还是配置文件…...

【深度学习】Huber Loss详解

文章目录 1. Huber Loss 原理详解2. Pytorch 代码详解3.与 MSELoss、MAELoss 区别及各自优缺点3.1 MSELoss 均方误差损失3.2 MAELoss 平均绝对误差损失3.3 Huber Loss 4. 总结4.1 优化平滑4.2 梯度较好4.3 为什么说 MSE 是平滑的 1. Huber Loss 原理详解 Huber Loss 是一种结合…...

Maven下载配置

目录 Win下载配置maven的环境变量 Mac下载安装配置环境变量 MavenSetting.xml文件配置 Win 下载 https://maven.apache.org/ 在主页面点击Download 点击archives 最好不要下载使用新版本&#xff0c;我使用的是maven-3.6.3&#xff0c;我们点击页面下方的archives&#xff0…...

JS基础(5):运算符和语句

一.运算符 1.赋值运算符 加减乘除都是一样的&#xff0c;&#xff0c;-&#xff0c;*&#xff0c;/ 2.一元运算符&#xff1a;经常用来计数 自增&#xff1a; 每次只能加一 自减&#xff1a;-- 前置自增 后置自增 结…...

游戏引擎学习第81天

仓库:https://gitee.com/mrxiao_com/2d_game_2 或许我们应该尝试在地面上添加一些绘图 在这段时间的工作中&#xff0c;讨论了如何改进地面渲染的问题。虽然之前并没有专注于渲染部分&#xff0c;因为当时主要的工作重心不在这里&#xff0c;但在实现过程中&#xff0c;发现地…...

网络安全 | 什么是正向代理和反向代理?

关注&#xff1a;CodingTechWork 引言 在现代网络架构中&#xff0c;代理服务器扮演着重要的角色。它们在客户端和服务器之间充当中介&#xff0c;帮助管理、保护和优化数据流。根据代理的工作方向和用途&#xff0c;代理服务器可分为正向代理和反向代理。本文将深入探讨这两种…...

前缀和——模板 二维前缀和

一.题目描述 【模板】二维前缀和_牛客题霸_牛客网 二.题目解析 这道题和上一道题有点异曲同工之妙。输入一个m行n列的矩阵&#xff0c;然后进行q次操作&#xff0c;每次操作输入4个数&#xff0c;作为两个点的坐标&#xff0c;计算这两个点为对角线的矩阵的和。 三.算法原理 …...

oracle使用case when报错ORA-12704字符集不匹配原因分析及解决方法

问题概述 使用oracle的case when函数时&#xff0c;报错提示ORA-12704字符集不匹配&#xff0c;如下图&#xff0c;接下来分析报错原因并提出解决方法。 样例演示 现在有一个TESTTABLE表&#xff0c;本表包含的字段如下图所示&#xff0c;COL01字段是NVARCHAR2类型&#xff0…...

高等数学学习笔记 ☞ 定积分与积分公式

1. 定积分的基本概念 1.1 定积分的定义 1. 定义&#xff1a;设函数在闭区间上有界。在闭区间上任意插入若干个分点&#xff0c;即&#xff0c; 此时每个小区间的长度记作(不一定是等分的)。然后在每个小区间上任意取&#xff0c;对应的函数值为。 为保证每段的值(即矩形面积)无…...

MLMs之Agent:Phidata的简介、安装和使用方法、案例应用之详细攻略

MLMs之Agent&#xff1a;Phidata的简介、安装和使用方法、案例应用之详细攻略 目录 Phidata简介 Phidata安装和使用方法 1、安装 2、使用方法 (1)、认证 (2)、创建 Agent (3)、运行 Agent (4)、Agent Playground Phidata 案例应用 1、多工具 Agent 2、多模态 Agent …...

如何在不暴露MinIO地址的情况下,用Spring Boot与KKFileView实现文件预览

在现代Web应用中&#xff0c;文件预览是一项常见且重要的功能。它允许用户在不上传或下载文件的情况下&#xff0c;直接在浏览器中查看文件内容。然而&#xff0c;直接将文件存储服务&#xff08;如MinIO&#xff09;暴露给前端可能会带来安全风险。本文将介绍如何在不暴露MinI…...

ESP8266固件烧录

一、烧录原理 1、引脚布局 2、引脚定义 3、尺寸封装 4、环境要求 5、接线方式 ESP8266系列模块集成了高速GPI0和外围接口&#xff0c;这可能会导致严重的开关噪声。如果某些应用需要高功率和EMI特性&#xff0c;建议在数字I/0线上串联10到100欧姆。这可以在切换电源时抑制过冲…...

Python 模拟真人鼠标轨迹算法 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…...

三天急速通关Java基础知识:Day1 基本语法

三天急速通关JAVA基础知识&#xff1a;Day1 基本语法 0 文章说明1 关键字 Keywords2 注释 Comments2.1 单行注释2.2 多行注释2.3 文档注释 3 数据类型 Data Types3.1 基本数据类型3.2 引用数据类型 4 变量与常量 Variables and Constant5 运算符 Operators6 字符串 String7 输入…...

免费使用 Adobe 和 JetBrains 软件的秘密

今天想和大家聊聊如何利用 Edu 教育邮箱免费使用 Photoshop、Illustrator 等 Adobe 系列软件&#xff0c;以及 JetBrains 开发工具。 首先&#xff0c;Adobe 的软件是设计师的必备工具。无论是处理图像的 Photoshop&#xff0c;还是进行矢量设计的 Illustrator&#xff0c;它们…...

Pytorch 自学笔记(三):利用自定义文本数据集构建Dataset和DataLoader

Pytorch 自学笔记&#xff08;三&#xff09; 1. Dataset与DataLoader1.1 torch.utils.data.Dataset1.2 torch.utils.data.DataLoader Pytorch 自学笔记系列的第三篇。针对Pytorch的Dataset和DataLoader进行简单的介绍&#xff0c;同时&#xff0c;介绍如何使用自定义文本数据集…...

gradle项目的创建和基本结构简介

文章目录 创建gradle项目&#xff08;命令行&#xff09;创建gradle项目&#xff08;IDEA&#xff09;项目基本结构和功能Gradle 构建流程测试类体验 创建gradle项目&#xff08;命令行&#xff09; yangMacdeMac-mini gradleStudy % gradle init Starting a Gradle Daemon (s…...

wow-agent---Day3 Zigent 智能代理开发框架

这个框架是课程讲解的&#xff0c;但资料比较少&#xff0c;觉得框架比较小众&#xff0c;所以这里只分析代码&#xff0c;打算把更多的精力放在metagpt的学习上&#xff0c;毕竟还是要学教为主流的框架&#xff0c;这对后续维护升级都有帮助&#xff0c;当然感兴趣作为研究&am…...

python 入门

1. Python 概述 1.1 简介 python 是一种面向对象的解释型编程语言&#xff0c;由吉多范罗苏姆开发&#xff1b; 1991 年&#xff0c;公开发行版发布&#xff1b; 因其可以将其他语言制作的模块轻松联接在一起&#xff0c;又被称作胶水语言&#xff1b; 1.2 优点 简单易学&…...

sentinel微服务保护

学习链接 SpringCloudRabbitMQDockerRedis搜索分布式 文章目录 学习链接1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结 1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel官网地址github地址…...

接口测试Day10-封装IHRM登录

-登录接口 普通方式实现 登录接口对象层 思路&#xff1a; 动态变化的&#xff0c;写入参数固定不变&#xff0c;直接写到方法实现内响应结果&#xff0c;通过返回值 return 分析&#xff1a; 封装实现&#xff1a; 登录接口测试用例层 封装断言方法 1、创建 文件 assert_uti…...

什么是IP地址、子网掩码、网关、DNS

简单解释 IP地址在网络中用于标识一个节点(或者网络设备的接口) IP地址用于IP报文在网络中的寻址 一个IPv4地址有32 bit。 IPv4地址通常采用“点分十进制”表示。 IPv4地址范围:0.0.0.0~255.255.255.255 网络部分:用来标识一个网络&#xff0c;代表IP地址所属网络。 主机部分:…...

python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)角点检测

角点检测&#xff08;Corner Detection&#xff09;是计算机视觉和图像处理中重要的步骤&#xff0c;主要用于提取图像中的关键特征&#xff0c;以便进行后续的任务&#xff0c;比如图像匹配、物体识别、运动跟踪等。下面介绍几种常用的角点检测方法及其应用。 1. Harris角点检…...

软路由系统iStoreOS 一键安装 docker compose

一键安装命令 大家好&#xff01;今天我来分享一个快速安装 docker-compose 的方法。以下是我常用的命令&#xff0c;当前版本是 V2.32.4。如果你需要最新版本&#xff0c;可以查看获取docker compose最新版本号 部分&#xff0c;获取最新版本号后替换命令中的版本号即可。 w…...

Invicti-Professional-V25.1

01 更新介绍 此更新包括对内部代理的更改。内部扫描代理的当前版本为 25.1.0。内部身份验证验证程序代理的当前版本为 25.1.0。#新功能现在&#xff0c;单击扫描摘要屏幕中的预设扫描图标会将您重定向到具有过滤视图的 “最近扫描” 页面&#xff0c;从而改进导航和对相关扫描…...

【QT】: 初识 QWidget 控件 | QWidget 核心属性(API) | qrc 文件

&#x1f525; 目录 1. 控件概述 控件体系的发展阶段 2. QWidget 核心属性 2.1 核心属性概览2.2 用件可用&#xff08;Enabled&#xff09; 2.3 坐标系&#xff08;Geometry&#xff09; **实例 1: 控制按钮的位置**实例 2: 表白 程序 2.4 窗口标题&#xff08;windowTiltle&a…...

Spring WebFlux

文章目录 一、概述1、Spring体系定位2、Spring MVC和WebFlux差异 二、入门1、依赖2、ReactorHttpHandlerAdapter&#xff08;main启动&#xff09;3、DispatcherHandler&#xff08;SpringWebFlux启动&#xff09;4、WebFilter 三、DispatcherHandler理解1、handle 前置知识&am…...

【AI | pytorch】torch.polar的使用

一、torch.polar的使用 torch.polar 是 PyTorch 中用来生成复数张量的一个函数&#xff0c;但它与数学中的复数表达式 ( z re^{i\theta} ) 是等价的。 具体来说&#xff0c;torch.polar(abs, angle) 接受两个实数张量参数&#xff1a; abs&#xff1a;表示复数的模长&#…...

AWTK fscript 中的 输入/出流 扩展函数

fscript 是 AWTK 内置的脚本引擎&#xff0c;开发者可以在 UI XML 文件中直接嵌入 fscript 脚本&#xff0c;提高开发效率。本文介绍一下 fscript 中的 iostream 扩展函数 1.iostream_get_istream 获取输入流对象。 原型 iostream_get_istream(iostream) > object示例 va…...

【多线程】线程池

一、什么是线程池 线程池&#xff08;Thread Pool&#xff09;是一种多线程并发执行的设计模式&#xff0c;它通过维护一个线程集合来执行多个任务&#xff0c;避免频繁地创建和销毁线程&#xff0c;提高系统性能和响应速度。 就好比如你经营了一家餐饮店&#xff0c;你名下有…...

小结:路由器配置和管理 RIP协议

华为路由器中配置和管理 RIP&#xff08;Routing Information Protocol&#xff09;的相关指令主要分为以下几个部分&#xff1a;基础配置指令、接口配置指令、路由控制指令和调试指令。以下是总结&#xff1a; 1. 启用 RIP 协议 [Huawei] rip 1rip 1&#xff1a;表示创建并启…...

基于海思soc的智能产品开发(高、中、低soc、以及和fpga的搭配)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 市场上关于图像、音频的soc其实非常多&#xff0c;这里面有高、中、低档&#xff0c;开发方式也不相同。之所以会这样&#xff0c;有价格的因素&am…...

于灵动的变量变幻间:函数与计算逻辑的浪漫交织(下)

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节我们主要来学习单个函数的声明与定义&#xff0c;static和extern… 这里写目录标题 一、单个函数…...

偷偷的学Java

序章&#xff1a;为何要偷偷学Java&#xff1f; • Java&#xff0c;不仅仅是一种编程语言 • 偷偷学Java&#xff0c;快速提升你的竞争力 •Java学习秘籍 第一章&#xff1a;Java的神秘面纱 •Java的起源与发展历程 •Java的生态系统与应用场景 • Java与其他编程语言的…...

以单用户模式启动 Linux 的方法

注&#xff1a;本文为 “Linux 启动单用户模式” 相关文章合辑。 未整理去重。 以单用户模式启动 linux 的三种方法 作者&#xff1a; Magesh Maruthamuthu 译者&#xff1a; LCTT Xiaobin.Liu 2020-05-03 23:01 单用户模式&#xff0c;也被称为维护模式&#xff0c;超级用户…...

【AI论文】生成式视频模型是否通过观看视频学习物理原理?

摘要&#xff1a;AI视频生成领域正经历一场革命&#xff0c;其质量和真实感在迅速提升。这些进步引发了一场激烈的科学辩论&#xff1a;视频模型是否学习了能够发现物理定律的“世界模型”&#xff0c;或者&#xff0c;它们仅仅是复杂的像素预测器&#xff0c;能够在不理解现实…...

【有啥问啥】Contrastive Captioners(CoCa):对比式图像描述模型——图像-文本基础模型的新范式

Contrastive Captioners(CoCa)&#xff1a;对比式图像描述模型——图像-文本基础模型的新范式 引言 随着深度学习技术的发展&#xff0c;多模态模型在处理视觉和语言任务方面的能力逐渐增强。特别是大规模预训练模型的兴起&#xff0c;使得这些模型可以快速迁移到许多下游任务…...

Python(十七)excel指定列自动翻译成英文

前言 本章主要讲述在excel的指定列后面添加一列&#xff0c;并翻译成英文 一、效果图 二、代码 实际需求&#xff1a; # -*- codeing utf-8 -*- # time: 2025/1/16 16:32 # Author : Mikasa # # Aim&#xff1a;自动将客户发的货物清单里的商品名称&#xff0c;翻译成英文…...

【论文投稿】探秘计算机视觉算法:开启智能视觉新时代

目录 引言 一、计算机视觉算法基石&#xff1a;图像基础与预处理 二、特征提取&#xff1a;视觉信息的精华萃取 三、目标检测&#xff1a;从图像中精准定位目标 四、图像分类&#xff1a;识别图像所属类别 五、语义分割&#xff1a;理解图像的像素级语义 六、计算机视觉…...

【机器学习实战入门】使用LSTM机器学习预测股票价格

机器学习在股票价格预测中有重要的应用。在这个机器学习项目中&#xff0c;我们将讨论如何预测股票的收益。这是一个非常复杂的任务&#xff0c;充满了不确定性。我们将会把这个项目分成两部分进行开发&#xff1a; 首先&#xff0c;我们将学习如何使用 LSTM 神经网络预测股票…...