你懂精通STM32的含金量吗?

描述

你懂精通ARM的含金量吗?你懂精通STM32的含金量吗?不管懂不懂都要懂,赶紧学。

寄存器

这是它的电源系统,下面会说这些引脚的作用

在STM32单片机中,每个寄存器都有一个独特的地址,这些地址是在芯片的数据手册中定义的。地址偏移是指每个寄存器的地址相对于其所在寄存器组的基地址的偏移量。

寄存器

在这里,每一个寄存器都有便宜的地址

例如,在STM32F407VG芯片中,GPIOB寄存器组的基地址为0x40020400。在这个寄存器组中,每个GPIOB端口的控制寄存器的地址偏移量是0x00、0x04、0x08、0x0C等。这意味着,如果您想访问GPIOB的第一个端口(PB0),则可以通过将地址偏移量0x00添加到GPIOB基地址(0x40020400)来访问它,即0x40020400 + 0x00 = 0x40020400。

在STM32中,使用寄存器地址偏移来访问和控制特定寄存器,这可以通过指针操作和位域操作等方式来实现。需要注意的是,对于不同的芯片型号和不同的寄存器组,其基地址和偏移量可能会有所不同。

在STM32单片机中,每个寄存器都有一个复位值(也称为默认值或初始值),这是当单片机复位时寄存器将被初始化为的值。复位值是在芯片的数据手册中定义的。

大多数寄存器的复位值都是0或某些特定的值,例如控制寄存器的复位值通常是0x0000或0x0001。

在单片机初始化过程中,为了确保寄存器处于正确的状态,需要将所有寄存器设置为其复位值。这可以通过在启动代码中执行清零操作或者使用STM32提供的库函数来实现。

一字节是计算机存储中的基本单位之一,通常包含8个比特(bit),每个比特只能表示0或1两种状态。在计算机中,一字节通常用于存储一个字符或者一个整数等数据。

在二进制表示中,一字节的取值范围是0~255(即00000000~11111111),因为它可以用8个比特组合出256种不同的状态,其中一个状态用于表示0。

一字节的大小在不同的计算机架构中可能会有所不同,但是在大多数计算机中,一字节都被定义为8个比特。此外,一些特殊的系统中,字节大小可能是其他值,如10比特或12比特。

一比特(bit)是计算机存储和通信中最小的数据单位,它只能表示0或1两种状态。比特通常用于表示二进制数据,在计算机中被广泛应用于数字电路和通信领域。

在二进制表示中,一个比特的取值范围是0或1,其中0表示低电平(或者“假”),1表示高电平(或者“真”)。比特的状态可以表示一位二进制数中的一位,因此8个比特组合起来可以表示一个字节,即一个8位的二进制数。

比特在计算机中有着广泛的应用,例如存储器单元中的存储单元就是由比特组成的,CPU中的寄存器也是由多个比特组合而成的。在通信领域,比特是衡量数据传输速率的单位,例如“1Mbps”表示每秒传输100万个比特。

这里写的就是上面的电源引脚,为什么在认知中简单的+,-两个而已,现在出来这么多脚?

Vdd和Vss是电子元器件中常用的术语,它们分别代表电路的正电源和负电源。

Vdd是指电路中的正电源,通常表示为Vcc,它为电路提供正电压,使电路中的器件工作。在数字电路中,Vdd通常为逻辑1的电压值,而在模拟电路中,Vdd通常为固定的直流电压源。

Vss是指电路中的负电源,也称为地线或接地线,通常表示为GND。Vss提供电路中的器件所需的负电压,以确保电路正常工作。在数字电路中,Vss通常为逻辑0的电压值,而在模拟电路中,Vss通常为固定的零电位或接地。

Vdd和Vss是电子电路中最基本的两个电源,电路中的其他信号、电压和电流都是基于这两个电源的。

Vdda是指模拟电路中的电源,通常用于提供模拟信号的参考电压。Vdda通常表示为VddA,与数字电路中的Vdd不同,Vdda通常为模拟电路所需的电压值,通常比数字电路中的Vdd更为精确和稳定。

Vdda通常用于ADC(模数转换器)和DAC(数模转换器)等模拟电路中。在ADC中,Vdda是参考电压,用于将模拟信号转换为数字信号。在DAC中,Vdda是数字信号转换为模拟信号时使用的参考电压。

与数字电路中的Vdd相比,Vdda的电压值通常要求更为精确和稳定,以确保模拟电路中的信号质量。

Vdd是指数字电路中的正电源,通常表示为Vcc,它为电路提供正电压,使电路中的器件工作。在数字电路中,Vdd通常为逻辑1的电压值。

Vdd电源通常用于数字逻辑电路中,例如微处理器、存储器、计数器等。Vdd的电压值取决于具体的器件要求和应用场景,通常在2V到5V之间,也有些器件需要更高的电压值。

在数字电路中,Vdd电源的稳定性和可靠性非常重要,如果Vdd电压不稳定或存在波动,可能会导致电路运行不稳定或出现错误。因此,在数字电路设计中,通常需要使用稳压电路或者其他电源管理技术来确保Vdd电压的稳定性和可靠性,以保证电路的正常工作。

Vssa是指模拟电路中的负电源,通常表示为VssA,它为模拟电路中的器件提供负电压,以确保模拟电路的正常工作。Vssa通常用于模拟信号的处理和放大等模拟电路中。

与数字电路中的Vss不同,Vssa通常需要比Vss更为精确和稳定,以确保模拟电路中的信号质量。在实际应用中,通常需要使用高精度的电源管理电路或其他电源稳定技术来确保Vssa的稳定性和可靠性,以确保模拟电路的正常工作。

需要注意的是,Vssa和VddA通常是相对于某个参考电位(通常是地线)而言的,它们之间的电压差通常称为模拟电源电压。

寄存器

这个简单,就是CubuMX的

在 STM32 数据手册中,寄存器的访问选项通常包括以下内容:

读/写权限:指示寄存器是否可读、可写或既可读又可写。

寄存器地址:指示寄存器在芯片内存映射中的地址。

寄存器位域:对于一些特殊的寄存器,可能会将其分为几个位域来表示不同的控制或状态信息。

复位值:指示寄存器在复位时的初始值。

等待周期:指示在对该寄存器进行读/写操作时需要等待多长时间才能获得结果。在一些情况下,需要等待芯片内部的一些操作完成后才能获得正确的结果,因此需要等待周期。

等待周期是指访问某些寄存器需要等待芯片内部操作完成的时间,通常称为访问延迟。在进行某些特殊操作时,需要等待周期才能确保数据的正确性。在进行读取操作时,等待周期可能包括寄存器响应时间、总线传输时间等等,具体时间可能会因为系统的配置和芯片的类型而有所不同。

在微控制器中,有时需要对存储器进行不同粒度的读写操作,字节(Byte)、半字(Half Word)和字(Word)是三种常见的数据粒度,它们分别表示存储器中的不同位数。具体含义如下:

字节(Byte):是存储器中的最小单元,通常表示为8位二进制数。字节可存储0-255的无符号整数,或者-128到127的有符号整数。字节访问意味着每次访问一个字节。

半字(Half Word):是由两个连续的字节组成,通常表示为16位二进制数。半字可存储0-65535的无符号整数,或者-32768到32767的有符号整数。半字访问意味着每次访问两个字节。

字(Word):是由四个连续的字节组成,通常表示为32位二进制数。字可存储0-4294967295的无符号整数,或者-2147483648到2147483647的有符号整数。字访问意味着每次访问四个字节。

在STM32微控制器中,为了支持不同粒度的读写操作,一些寄存器和存储器区域提供了不同的访问选项,例如:

8位字节访问(BYTE):每次访问8位(1个字节)的数据。

16位半字访问(HALFWORD):每次访问16位(2个字节)的数据。

32位字访问(WORD):每次访问32位(4个字节)的数据。

在 STM32 微控制器中,系统时钟和外设时钟都是由一个基础时钟源衍生出来的,对于 STM32 系列微控制器而言,通常都是使用内部的 RC 振荡器或者外部的晶体振荡器作为基础时钟源。

时钟中断寄存器是一类特殊的寄存器,用于配置 STM32 微控制器中的时钟中断。时钟中断是指在系统时钟或者外设时钟到达某个特定时间或者计数值时产生的一种中断信号,用于触发某些特定的操作或者执行周期性的任务。

STM32 微控制器中的时钟中断寄存器通常包括以下内容:

时钟控制寄存器:用于配置时钟的各种参数,例如时钟源选择、时钟分频系数、时钟计数器等等。

中断控制寄存器:用于配置时钟中断的触发条件和中断优先级等参数。

中断状态寄存器:用于记录当前是否有时钟中断发生,并且可以清除中断标志位。

在 STM32 微控制器中,不同的系列和型号会有不同的时钟中断寄存器,具体的使用方法和配置参数需要根据具体的型号和应用场景进行选择和调整。

寄存器

复位电路

寄存器

时钟树在面试的时候也会问,这个其实就是多看看就好了

寄存器

在HSE时钟上面的晶振如何接

寄存器

在MX上面生成调试的时候,需要选择这个

寄存器

会有默认的引脚来启用

寄存器

可以选择一个IIC的

寄存器

下面是具体的一些功能,可以去设置,在代码中有体现

寄存器

IIC默认开启的是这两个引脚

寄存器

有时候引脚之间是互相冲突的

寄存器

可以试试UART

寄存器

硬件外设就直接打开

寄存器

这些是串口的常见设置

寄存器

你可以点右边来设置精确的功能

寄存器

在这里被设置

寄存器

当然DMA如果要开启的时候也是可以的

寄存器

时钟

寄存器

这是一些设置

寄存器

为每个外设生成H,C

寄存器

也可以选取LL低级性能库

寄存器

第一次找到这个功能,好像是打开回调

寄存器

寄存器

代码在之间

寄存器

这个就是生成的函数

寄存器

里面的中断函数

寄存器

IO脚的设置自己看嘛

寄存器

4部分

寄存器

01控制

寄存器

速度控制,说了100遍

寄存器

也可以选择JTAG

寄存器

默认复位的功能

寄存器

一次用4个线,不配了

寄存器

引脚的配置

寄存器

自己看

寄存器

输入模式

在 STM32 微控制器中,APB2(Advanced Peripheral Bus 2)是一个高速外设总线,它连接了一些重要的外设模块,例如定时器、串行通信接口、通用串行总线控制器等等。APB2 的时钟频率可以通过时钟树的分频器进行配置,因此可以实现不同外设模块之间的时序控制和数据交换。

STM32 APB2 外设包括但不限于以下几个:

定时器:STM32 微控制器中有多种不同类型的定时器,包括基本定时器、通用定时器、高级定时器等等,这些定时器可以用于产生各种精度和周期的定时器中断,同时还支持 PWM 信号输出、脉冲计数等功能。

串行通信接口:STM32微控制器中支持多种不同的串行通信协议,包括 UART、SPI、I2C等等,这些外设模块可以实现与其他设备的数据通信和控制。

通用串行总线控制器:STM32 微控制器中的通用串行总线控制器(USB OTG FS)是一种高速的通用串行总线接口,可以实现与 USB 设备的通信和数据传输。

ADC:STM32 微控制器中的 ADC(模数转换器)可以用于采集模拟信号并将其转换为数字信号,提供多种不同的采样率和分辨率选择。

DMA:STM32 微控制器中的 DMA(直接存储器访问)模块可以实现高效的数据传输和数据存储,同时也可以降低 CPU 的负载,提高系统性能。

在STM32中,APB2是高速外设总线,例如SPI、I2S、USART、ADC等,其时钟频率通常比APB1和AHB总线更高。在每个APB2时钟上采样指的是在APB2时钟周期内对某个外设进行多次采样以提高采样精度和减小采样误差的技术。

例如,在使用STM32的ADC(模数转换器)时,可以选择在每个APB2时钟上采样来提高转换精度。这样做的好处是可以将ADC的采样周期与APB2总线的时钟同步,提高ADC采样率,减小噪声和抖动对采样的影响,从而提高系统的性能和稳定性。

在STM32中,IRQ是指中断请求(Interrupt Request)。它是一种特殊的处理器信号,用于通知CPU有重要的事件需要立即处理。当一个中断事件被触发时,它会向处理器发出IRQ信号,将处理器从正常的程序执行中打断,转而执行预定义的中断服务程序(ISR)来处理中断事件。

STM32芯片具有许多外设(例如定时器、串口、ADC等),这些外设可以通过配置相应的中断请求(IRQ)来实现异步事件的处理。当外设产生了预定义的事件(例如定时器计数器溢出、串口接收数据等)时,它会自动向中断控制器发出IRQ信号,触发中断请求,并使得CPU暂时停止执行正在运行的程序,转而执行相应的中断服务程序来处理该事件。

在STM32中,IRQ信号由中断控制器(NVIC,Nested Vector Interrupt Controller)进行管理和分配,它可以对外设产生的不同类型的中断请求进行优先级排序和处理。

异步编程是一种编程模型,它不依赖于线程的同步和阻塞等机制,而是通过回调函数、事件驱动等方式来实现代码的非阻塞执行,从而提高系统的并发性能和响应速度。

异步编程通常涉及到异步操作和异步实践两个概念。

异步操作指的是一种不会阻塞当前线程的操作,例如IO操作、网络请求等。异步操作一般是通过异步函数或异步API来实现的,它们通常会在后台启动一个或多个线程或进程来处理请求,从而避免当前线程被阻塞。异步操作通常会在完成后触发一个事件或回调函数,通知应用程序异步操作已经完成,并将操作结果传递给应用程序。

异步实践指的是使用异步编程模型来优化系统性能和响应速度的一系列技术和方法。异步实践的核心思想是将那些可能导致阻塞的操作(例如IO操作、数据库访问等)转换为异步操作,以保证系统的高并发性能和响应速度。异步实践还包括一些常见的技术和方法,例如使用事件驱动、异步回调、异步消息队列等,来实现代码的异步执行和解耦合。

在嵌入式开发中,异步实践也非常重要。例如,使用STM32等微控制器进行嵌入式开发时,可以使用异步编程模型和异步实践技术来实现对外设的异步控制和数据采集等功能,从而提高系统的性能和可靠性。

1.使用异步函数或异步API:嵌入式系统中的许多操作都是耗时的,例如IO操作、网络通信等,如果使用同步方式进行处理,会导致当前线程被阻塞,影响系统的响应速度。因此,在嵌入式系统中,通常使用异步函数或异步API来实现这些操作。异步函数或API会在后台启动一个或多个线程或任务来处理请求,从而避免当前线程被阻塞,同时也可以提高系统的并发性能和响应速度。

2.使用中断机制:嵌入式系统中,许多操作都是通过中断机制来实现的,例如定时器中断、外部中断等。使用中断机制可以使得系统不必等待某些操作的完成,而是在该操作完成时自动触发中断处理程序,从而实现异步处理。

3.使用事件驱动模型:事件驱动模型是一种基于事件和回调函数的编程模型,它通过监听特定的事件来触发对应的回调函数。在嵌入式系统中,事件驱动模型可以用来实现异步操作的回调函数。例如,在处理串口通信时,可以使用事件驱动模型监听串口接收数据的事件,并在数据到达时触发对应的回调函数进行数据处理。

4.使用消息队列:消息队列是一种将消息异步传递给消费者的机制。在嵌入式系统中,可以使用消息队列来实现异步任务的处理。例如,在处理图像识别时,可以使用消息队列来异步处理图像数据,从而避免阻塞当前任务的执行。

引脚的开漏模式是指在输出电平时,将该引脚连接的晶体管的源极或发射极与地相连,使得该引脚的输出电平只能为低电平(0V)或高阻态(电阻很大,相当于没有连接)。开漏模式的引脚通常被称为开漏输出引脚。

在开漏模式下,输出引脚不会直接提供高电平的输出,而是通过连接一个上拉电阻或使用内部上拉电阻来实现。上拉电阻的值通常很大,因此在输出高电平时,输出电平可能不稳定,容易受到外部干扰的影响。

开漏模式的引脚通常用于控制外部电路中的开关元件(例如晶体管、继电器等),以及驱动I2C总线等外部设备。在这些场景中,开漏模式的引脚可以通过控制开关元件的导通和断开来实现对外部设备的控制,同时也可以避免由于外部设备的反向电流等问题对系统的损害。

需要注意的是,开漏模式的引脚在输出高电平时不能直接驱动负载,需要通过使用外部上拉电阻或使用内部上拉电阻来实现,同时在设计电路时需要考虑到该引脚的输出电流和负载电流的匹配,以避免对系统的损害。

引脚的推挽模式是指在输出电平时,将该引脚连接的晶体管的源极或发射极与电源相连,使得该引脚的输出电平可以为高电平(电源电压)或低电平(0V)。推挽模式的引脚通常被称为推挽输出引脚。

在推挽模式下,输出引脚可以直接提供高电平和低电平的输出,并且输出电流通常比较大,可以直接驱动一定的负载电流。

推挽模式的引脚通常用于控制外部电路中的驱动元件(例如LED、直流电机等),以及驱动SPI总线等外部设备。在这些场景中,推挽模式的引脚可以通过控制驱动元件的导通和断开来实现对外部设备的控制,同时也可以提供较大的输出电流以驱动负载电流。

需要注意的是,在推挽模式下,引脚的输出电平不能为高阻态,需要确保连接的晶体管在输出低电平时处于关闭状态,否则可能导致电路不稳定或电路损坏。

相同点:

都可以作为引脚的输出模式,输出高电平和低电平的电压信号;

都可以用来控制外部电路中的驱动元件,例如LED、直流电机等。

不同点:

开漏模式的引脚输出电平只能为低电平或高阻态,需要通过上拉电阻来实现高电平的输出;推挽模式的引脚输出电平可以为低电平或高电平;

推挽模式的引脚具有较大的输出电流能力,可以直接驱动负载电流,而开漏模式的引脚需要通过上拉电阻来驱动负载电流;

开漏模式的引脚通常用于控制外部电路中的开关元件(例如晶体管、继电器等),以及驱动I2C总线等外部设备;而推挽模式的引脚通常用于控制外部电路中的驱动元件(例如LED、直流电机等),以及驱动SPI总线等外部设备。

推挽模式的引脚适合需要较大输出电流的场景,例如驱动LED、直流电机等负载,同时也适合驱动SPI总线等外部设备;

开漏模式的引脚适合需要通过控制开关元件的导通和断开来实现控制的场景,例如驱动I2C总线等外部设备。

当外设模块需要从CPU读取数据时,它需要按照APB2总线时钟的节奏来发送读取请求,并在每个APB2时钟周期中传输一个数据位,直到所有数据都被读取完毕为止。

施密特触发输入是一种常见的数字电路输入技术,也被称为施密特触发器输入或双稳态输入。施密特触发输入通过特殊的电路设计,能够使输入信号的干扰和抖动不会轻易导致输出信号的误判或不稳定。

施密特触发输入的基本原理是在输入信号的电压上下阈值范围内,输出信号维持稳定的高或低电平;而当输入信号超过一定的阈值范围时,输出信号则发生明显的翻转,并维持到输入信号回到另一个阈值范围时才再次翻转。这种特性可以有效地防止输入信号的干扰和抖动导致输出信号的误判或不稳定。

施密特触发输入常见的应用场景包括数字信号的输入、信号的去抖动处理、信号的滤波处理等。在数字电路的设计和实现中,施密特触发输入可以提高电路的抗干扰能力和稳定性,从而提高数字系统的性能和可靠性。

P-MOS

P-MOS是一种由p型半导体材料制成的MOSFET晶体管,其控制方式是通过在栅极和源极之间施加负电压来控制晶体管的导通状态。当栅极电压低于源极电压时,P-MOS导通;当栅极电压高于源极电压时,P-MOS截止。P-MOS的导通电阻相对较大,通常用于高电平驱动电路中。

N-MOS

N-MOS是一种由n型半导体材料制成的MOSFET晶体管,其控制方式是通过在栅极和源极之间施加正电压来控制晶体管的导通状态。当栅极电压高于源极电压时,N-MOS导通;当栅极电压低于源极电压时,N-MOS截止。N-MOS的导通电阻相对较小,通常用于低电平驱动电路中。

弱上拉是指在输入端口(比如微控制器的GPIO口)上通过加入一个大约为10kΩ的电阻,从而使该输入端口与VCC(正电源)之间形成一个电阻分压网络,从而使输入端口的电压在没有外部信号的情况下趋向于高电平,即被上拉到VCC电平。

弱上拉的作用在于,当没有外部信号输入时,输入端口会被保持在一个已知的状态,从而有效地避免了输入端口因为外部信号干扰而导致的错误输入。同时,在需要对输入端口进行读取的时候,由于弱上拉电阻的存在,输入端口的状态可以更容易地被检测到。

我们其实就是操作IO的输出和输入,各种功能到底怎么样的配置?

寄存器寄存器

这么多的定时器,让我来给大家写一写

STM32定时器的输入捕获功能可以用来测量外部信号的时间间隔、频率和脉宽等信息,常用于测量脉冲信号、编码器信号、PWM信号等。

具体实现步骤如下:

配置定时器为输入捕获模式。

配置定时器的输入捕获通道,选择输入捕获边沿(上升沿或下降沿)触发测量。

在输入捕获中断服务函数中读取捕获寄存器的值,计算出测量值。

根据需要,可以通过中断或DMA方式进行多次测量,并对测量结果进行平均值计算等处理。

下面是一个基于STM32 HAL库的输入捕获示例代码,以测量外部脉冲信号的周期和脉宽为例:

 

#include "stm32f4xx_hal.h"


TIM_HandleTypeDef htim2;
uint32_t pulse_width = 0;
uint32_t period = 0;


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2)
    {
        static uint32_t last_capture = 0;
        uint32_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        uint32_t diff = capture - last_capture;
        last_capture = capture;
        
        if (diff > 0) {
            period = diff;
        } else {
            pulse_width = -diff;
        }
    }
}


int main(void)
{
    HAL_Init();
    __HAL_RCC_TIM2_CLK_ENABLE();
    
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 83; // 84MHz / 84 = 1MHz
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 0xFFFF;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&htim2);


    TIM_IC_InitTypeDef sConfigIC;
    sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE;
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    sConfigIC.ICFilter = 0;
    HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);


    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);


    while (1)
    {
        // do something
    }
}

 

在上述代码中,使用了TIM2定时器的通道1进行输入捕获,并开启了中断模式(HAL_TIM_IC_Start_IT函数)。在输入捕获中断服务函数中,计算出上一次和本次捕获的时间差,根据时间差的正负值来区分计算周期还是脉宽,最终得到测量结果。

STM32定时器的输出比较功能可以用来生成PWM波形、产生触发信号、实现周期性的定时器中断等。其实现步骤如下:

配置定时器为输出比较模式,并选择输出比较通道。

配置定时器的时基参数,包括计数器的时钟频率、计数器的计数范围等。

配置输出比较模式下的通道参数,包括输出模式(比较输出或PWM输出)、比较值等。

启动定时器,使其开始计数并产生输出信号。

下面是一个基于STM32 HAL库的输出比较示例代码,以生成50%占空比、1kHz的PWM信号为例:

 

#include "stm32f4xx_hal.h"


TIM_HandleTypeDef htim2;


void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();


        GPIO_InitStruct.Pin = GPIO_PIN_5;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
}


void MX_TIM2_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_OC_InitTypeDef sConfigOC = {0};


    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 83; // 84MHz / 84 = 1MHz
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 999; // 1kHz PWM
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&htim2);


    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);


    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);


    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 499; // 50% duty cycle
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
}


int main(void)
{
    HAL_Init();
    MX_TIM2_Init();
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
    
    while (1)
    {
        // do something
    }
}

 

STM32定时器的互补输出功能可以用来实现半桥或全桥逆变器、电机驱动器等应用。它通过在同一个定时器中同时配置两个输出比较通道,一个通道输出高电平,另一个通道输出低电平,以实现互补输出的功能。

下面是一个基于STM32HAL库的互补输出示例代码,以实现半桥逆变器为例:

 

#include "stm32f4xx_hal.h"


TIM_HandleTypeDef htim1;


void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (htim->Instance == TIM1)
    {
        __HAL_RCC_TIM1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_GPIOE_CLK_ENABLE();


        GPIO_InitStruct.Pin = GPIO_PIN_8;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


        GPIO_InitStruct.Pin = GPIO_PIN_13;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
        HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    }
}


void MX_TIM1_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_OC_InitTypeDef sConfigOC = {0};


    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 839; // 84MHz / 840 = 100kHz
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 9999; // 10Hz PWM
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim1.Init.RepetitionCounter = 1; // 2 channels, repetition count = 1
    HAL_TIM_PWM_Init(&htim1);


    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);


    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);


    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 4999; // 50% duty cycle
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2);


    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
}


int main(void)
{
    HAL_Init();
    MX_TIM1_Init();
    
    while (1)
    {
       }
 }

 

STM32定时器的刹车输入是用于实现电机或马达控制器的紧急停止或刹车功能。当刹车输入信号被触发时,定时器立即停止计数,并且输出保持在一个预定义的状态(例如输出低电平或高电平)。在应用中,刹车输入通常是由硬件电路(如电机控制器)提供的。

STM32定时器的刹车输入通常与定时器的输入捕获或输出比较功能结合使用。例如,在一个三相电机驱动器中,定时器的输入捕获功能用于捕获电机转速,输出比较功能用于产生PWM波形驱动电机,而刹车输入则用于实现急停功能。

下面是一个基于STM32 HAL库的刹车输入示例代码:

 

#include "stm32f4xx_hal.h"


TIM_HandleTypeDef htim1;


void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (htim->Instance == TIM1)
    {
        __HAL_RCC_TIM1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();


        GPIO_InitStruct.Pin = GPIO_PIN_8;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
}


void MX_TIM1_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_OC_InitTypeDef sConfigOC = {0};


    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 839; // 84MHz / 840 = 100kHz
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 999; // 1kHz PWM
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&htim1);


    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);


    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);


    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 499; // 50% duty cycle
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);


    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}


int main(void)
{
    HAL_Init();
    MX_TIM1_Init();
    
    while (1)
    {
        // Main loop
    }
}


void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    {
        // Handle PWM pulse finished event
    }
}

 

STM32定时器的外部触发时钟输入是一种使定时器以外部信号作为计数时钟的功能。它允许定时器在外部触发信号到来时开始计数,并且不依赖于内部时钟。这种功能常用于需要精确时间测量或同步的应用,如数据采集、PWM控制和通信接口等。

STM32定时器的外部触发时钟输入可通过使用TIMx_ETR (外部触发器)引脚来实现。在应用中,可以选择使用外部触发器引脚(例如TIM1_ETR),然后使用寄存器配置定时器的触发模式和触发源。

下面是一个基于STM32 HAL库的外部触发时钟输入示例代码:

 

#include "stm32f4xx_hal.h"


TIM_HandleTypeDef htim1;


void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1)
    {
        __HAL_RCC_TIM1_CLK_ENABLE();
    }
}


void MX_TIM1_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};


    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 8399; // 84MHz / 8400 = 10kHz
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 999; // 10Hz timer
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim1);


    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETRMODE2;
    sClockSourceConfig.ClockPolarity = TIM_CLOCKPOLARITY_NONINVERTED;
    sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;
    sClockSourceConfig.ClockFilter = 0;
    HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);


    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);


    HAL_TIM_Base_Start(&htim1);
}


int main(void)
{
    HAL_Init();
    MX_TIM1_Init();


    while (1)
    {
        // Main loop
    }
}

 

TM32定时器是一种用于计时和控制输出信号的硬件模块,它具有多种功能和操作模式。STM32定时器的原理是利用一个计数器(Counter)来计数,当计数器的值达到某个阈值时,就会产生一个中断或触发一个事件。其中,计数器的计数基准由定时器时钟源(Timer Clock Source)提供,可以是内部时钟(例如APB1时钟)或外部时钟(例如外部晶振)。

STM32定时器的计数器可以通过多种方式进行控制和配置,其中包括:

寄存器

接着看看串口的功能

分频器(Prescaler):通过分频器可以将定时器时钟源的频率降低,从而改变计数器的计数速度。分频器的分频值由预分频器寄存器(PSC)设置,它是一个16位寄存器。

自动重载寄存器(Auto-reload Register):自动重载寄存器(ARR)存储定时器计数器的最大值。当计数器的计数值达到自动重载寄存器的值时,计数器会重新从零开始计数。这种方式通常用于周期性产生中断或控制PWM波形的占空比。

捕获寄存器(Capture Register):捕获寄存器(CCR)用于存储定时器计数器的当前值。当捕获到一个特定事件时,捕获寄存器会自动被更新。这种方式通常用于测量外部事件的持续时间或频率,例如测量脉冲宽度或计算输入信号的频率。

比较寄存器(Compare Register):比较寄存器(CCR)用于存储与计数器进行比较的值。当计数器的值达到比较寄存器的值时,会触发中断或控制输出信号的状态。这种方式通常用于产生精确的定时事件或控制PWM波形的频率和占空比。

TM32的UART模块支持全双工模式,其中数据可以在同一个UART通道上同时发送和接收。

要使用STM32的UART模块进行全双工通信,您需要进行以下设置:

配置UART模块的时钟和波特率,使其能够与外部设备通信。

配置UART模块的发送和接收引脚,以便将数据发送到外部设备并从外部设备接收数据。

配置UART模块的工作模式为全双工模式,这可以通过将UART模块的USART_InitTypeDef结构体中的Mode字段设置为USART_MODE_TX_RX来实现。

在程序中编写发送和接收函数,以便发送和接收数据。

以下是一个使用STM32的UART模块进行全双工通信的示例代码:

 

#include "stm32f10x.h"
#include 


USART_InitTypeDef USART_InitStructure;


void USART1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);


    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);


    USART_Cmd(USART1, ENABLE);
}


void USART1_SendChar(char ch)
{
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, ch);
}


char USART1_ReceiveChar(void)
{
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    return USART_ReceiveData(USART1);
}


int main(void)
{
    char received_char;
    USART1_Init();
    
    while(1)
    {
        //发送数据
        USART1_SendChar('H');
        USART1_SendChar('e');
        USART1_SendChar('l');
        USART1_SendChar('l');
        USART1_SendChar('o');
        USART1_SendChar('
');
        
        //接收数据
        received_char = USART1_ReceiveChar();
        printf("Received: %c
", received_char);
    }

 

单工模式(Simplex Mode):在单工模式下,串口只能在一个方向上传输数据,即数据只能单向发送或接收。在单工模式下,无法同时发送和接收数据。这种模式的应用场景较为有限,通常只用于简单的数据采集或监测任务。

半双工模式(Half Duplex Mode):在半双工模式下,串口可以在两个方向上传输数据,但不能同时进行。也就是说,数据可以在发送和接收之间切换,但不能同时进行。这种模式广泛应用于需要双向通信的应用场景,例如工业自动化和机器人控制。

全双工模式(Full Duplex Mode):在全双工模式下,串口可以在两个方向同时传输数据,也就是说,可以同时发送和接收数据。这种模式的应用场景最为广泛,通常用于数据通信、网络通信等领域。

不同串口模式的异同在于其能否同时进行数据的发送和接收。单工模式只能单向传输数据,半双工模式可以双向传输数据但不能同时进行,全双工模式可以同时进行数据的双向传输。因此,在实际应用中,需要根据具体的场景和需求选择适合的串口模式。

寄存器

SPI全双工模式:在SPI全双工模式下,数据可以同时在主机和从机之间传输。主机通过发送时钟信号来控制数据传输,每次传输一个字节或者一个字(16位)。SPI全双工模式是最常用的SPI模式,应用于数据传输速度要求较高、需要双向数据传输的场景。

SPI半双工模式:在SPI半双工模式下,数据只能在主机和从机之间单向传输。主机先发送数据,然后从机接收数据,或者从机先发送数据,然后主机接收数据。SPI半双工模式应用于一些只需要单向数据传输的场景,例如LED显示、IO扩展等。

SPI主机模式:在SPI主机模式下,主机控制SPI通信的时序和数据传输,从机被动接受并响应主机的控制。SPI主机模式常用于控制多个SPI从机的场景。

SPI从机模式:在SPI从机模式下,从机被动接受来自主机的控制和数据传输。SPI从机模式应用于一些只需要单个从机的场景。

不同SPI模式的异同在于其能否同时进行双向数据传输,以及数据传输时的时序和控制方式。

SPI全双工模式和半双工模式的主要区别在于双向传输的能力,而SPI主机模式和从机模式的主要区别在于SPI通信的控制者是主机还是从机。

SPI(Serial Peripheral Interface,串行外围设备接口)是一种简单的、高速的串行通信协议,用于在微控制器和外部设备之间传输数据。SPI协议是一种同步协议,数据在时钟信号的控制下进行传输。

SPI协议的基本通信原理是:

在SPI通信中,需要至少两个设备,一个作为主设备,另一个或多个作为从设备。主设备负责控制SPI通信的时序和数据传输,从设备被动接受并响应主设备的控制。

SPI通信中有四条信号线:SCK、MOSI、MISO和SS。SCK是时钟信号线,由主设备产生;MOSI是主设备发送数据到从设备的信号线;MISO是从设备发送数据到主设备的信号线;SS是片选信号线,用于选择从设备。在多个从设备的情况下,每个从设备都需要有一个独立的片选信号线。

在SPI通信中,主设备产生时钟信号,每个时钟周期传输一个比特位,总线的数据传输是由主设备控制的。主设备先选择一个从设备,即在对应的片选信号线拉低,然后向从设备发送数据。从设备在接收到数据后,根据协议进行响应,并将响应数据发送给主设备。主设备接收完从设备的响应数据后,选择下一个从设备或结束通信。

SPI协议的优点是传输速度快、控制简单、通信稳定可靠。SPI通信速度可以达到几十MHz,具有很高的实时性和实时控制性能。

寄存器

寄存器

寄存器

寄存器

三瓜俩枣的价格还给个USB

寄存器

USB OTG(On-The-Go)控制器:USB OTG控制器可以支持主机和设备两种USB模式,因此可以在连接不同USB设备时自动识别主机和设备角色,并切换到相应的模式。USB OTG控制器支持USB 2.0标准,能够实现高速(480 Mbps)、全速(12 Mbps)和低速(1.5 Mbps)的数据传输。此外,USB OTG控制器还支持Suspend和Resume模式,可以使设备在空闲状态下降低功耗。

USB设备接口:STM32F103系列微控制器还内置了USB设备接口,可以直接连接到PC或其他USB主机设备。在这种模式下,微控制器可以作为USB设备与主机进行通信,如传输数据、控制外围设备等。USB设备接口支持USB 2.0标准,能够实现高速、全速和低速的数据传输。

USB引导加载器:STM32F103系列微控制器还支持通过USB引导加载器(USB bootloader)对程序进行烧录,这种方式不需要额外的烧录器件,只需通过USB接口即可进行程序下载。这种方式便于生产线上的批量烧录和固件更新。

USB DMA(Direct Memory Access)控制器:STM32F103系列微控制器还支持USB DMA控制器,可以实现高效的USB数据传输。DMA控制器能够直接将USB数据传输到内存中,从而减少CPU的负担,提高数据传输效率。

寄存器寄存器

寄存器

完事了家人们

SDIO是一种标准的接口协议,它可以用于SD卡,MMC(MultiMediaCard)卡等存储卡的数据交换。STM32F103的SDIO接口支持SD卡的SD1.0、SD1.1、SD2.0和SD3.0标准,最高传输速度可达到48Mbps。

SDIO接口的主要功能包括:

初始化:配置SDIO时钟、总线宽度、传输模式、数据超时等参数。

SD卡识别:通过发送CMD0命令使SD卡进入Idle状态,并发送CMD8命令获取SD卡的电压范围和支持的接口版本。

卡信息读取:通过发送CMD9命令读取SD卡的CSD(Card Specific Data)寄存器和CID(Card Identification)寄存器,获取SD卡的制造商信息、容量、传输速率等信息。

数据读写:通过发送CMD17和CMD18命令读取SD卡的数据块,通过发送CMD24和CMD25命令写入数据块。

卡状态监测:通过发送CMD13命令查询SD卡的状态,包括卡是否准备好、当前传输状态、错误状态等。

DMA传输:STM32F103的SDIO接口支持DMA传输,可以减少CPU的负担,提高数据传输效率。

最后两个,打完收工:

寄存器寄存器

寄存器

谁懂啊,家人们

寄存器

输入和输出

定时器这块没有完全吃透,准备先写51或者是MSP430的时钟系统。

寄存器

这个就是定时器的数据手册,不知道为什么倒着写

寄存器

①CK_PSC是定时器时钟TIMxCLK,经APB1预分频器后分频提供。

②定时器时钟经过PSC 预分频器之后,即CK_CNT,用来驱动计数器计数。

③计数器CNT 是一个16 位的计数器,向上,向下,向上/下计数模式,最大计数值为65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。

④自动重装载寄存器ARR 是一个16位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。

寄存器

计数器信号

其中CK_CNT时钟就类似心跳,CNT计数器就类似心跳次数。要实现60秒定时,CK_CNT是1s,我们设置CNT计数器向上计数开启中断,因为只有溢出时,也就是计数到65535时才会有中断,那么我们设置CNT计数器为65535-60=65475,开始计时,那么60秒后就会产生中断。

设置自动重装载寄存器ARR也为65475,当CNT计数器溢出时,自动重装载寄存器ARR就会自动装载到CNT计数器中,就能实现自动循环定时60秒。经过上面分析,精确定时的关键在于CK_CNT的频率,而CK_CNT是由定时器时钟分频而来的。

寄存器

寄存器

注:为什么需要中间对齐模式:

在永磁同步电机的控制中,需要对电机的三相定子施加一定的电压,才能控制电机转动。现在用的较多的是SVPWM(SVPWM的具体原理会在后面另写一篇博客说明),要想产生SVPWM波形,需要控制的三相电压呈如下形式,即A、B、C三相的电压是中间对齐的,这就需要用到stm32定时器的中间对齐模式了。

寄存器






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分