首先我们学习用的stm32这个是意法半导体产出的。

我们使用最新的STM32CubeIDE就可以实现芯片选择外设配置,代码生成,代码编译,程序烧录,程序调试这样的全过程

第一次使用要登录才能够下载对应芯片的固件安装包

我们本个选择的芯片是STM32F103C8T6,里面的ST是意法半导体,M32是32位寄存器,F1是主流,03是3系列,C是48引脚,8->64kb的flash存储,T是温度范围,6是引脚镀层种类

这里我们养成一个习惯,每次打开ide都在RCC(Reset and Clock Control复位和时钟控制)里面设置为Crystal/Ceramic Resonator(晶振,陶瓷振荡器)

在SYS(system系统模块)里面设置Debug为串行线Serial Wire

  • STM32 芯片一般有两种调试接口:

    • JTAG(需要 4~5 根线,功能多,但是占用 IO 引脚多)

    • Serial Wire (SWD)(只需要 2 根线:SWDIO 和 SWCLK,更常用)

  • 你这里选了 Serial Wire,就是告诉芯片用 SWD 接口进行调试和下载程序。

1,首先点亮小灯

什么是GPIO引脚:General Purpose Input Output 通用输入输出引脚

(这里的PA----PB都是GPIO引脚)

我们在原理图里面给三个小灯分别设置PA6,PA7,和PB0来控制(这里我们使用的是推挽输出)

(每个元件需要的电流不一样所以电阻不一样)

设置CubeIDE图形化里面的引脚为输出的时候

左键

(看下面这些的时候我们可以先把上面的图截屏放在一侧来看)

1. Reset_State

  • 功能
    引脚处于复位状态,表示没有被配置为任何特定功能。
  • 特点
    • 引脚处于默认状态(通常是高阻态)。
    • 引脚不参与任何外设或功能。
    • 如果没有配置引脚的用途,通常会保持这种状态。

2. ADC1_IN14

  • 功能
    配置为 ADC(模数转换器)1的第14路输入通道,用于采集模拟信号并转换为数字信号
  • 特点
    • 适用于连接传感器或其他模拟设备。
    • 模拟信号范围通常是 0V 到 VREF(参考电压)。
    • 在 STM32 内部通过 ADC1 模块处理采集的信号。

3. ADC2_IN14

  • 功能
    配置为 ADC(模数转换器)2的第14路输入通道,功能与 ADC1 类似,但使用的是 ADC2 模块。
  • 特点
    • 可以实现多通道采集,或者通过多个 ADC 实现更高采样率。
    • 同样用于处理模拟信号。

4. GPIO_Input

  • 功能
    配置为 GPIO(通用输入输出)输入模式,用于接收外部数字信号
  • 特点
    • 可以读取外部设备的状态,比如按键、传感器等。
    • 可以设置为带上拉或下拉电阻,避免悬空状态导致的信号不稳定。

5. GPIO_Output

  • 功能
    配置为 GPIO 输出模式,用于输出数字信号,驱动外部设备。
  • 特点
    • 可以控制外部设备,比如 LED、继电器等。
    • 支持推挽输出和开漏输出两种方式。

6. GPIO_Analog

  • 功能
    配置为 GPIO 模拟模式,允许引脚处理模拟信号
  • 特点
    • 适用于 ADC 或 DAC(数模转换器)等模拟功能。
    • 在模拟模式下,通常引脚不会有数字逻辑特性。

7. EVENTOUT

  • 功能
    配置为事件输出,用于将事件信号输出到引脚。
  • 特点
    • 常用于低功耗模式下的事件唤醒。
    • 一般和 EXTI(外部中断)结合使用。

8. GPIO_EXTI4

  • 功能
    配置为 EXTI(外部中断)4通道,用于处理外部中断信号。
  • 特点
    • 可用于捕获外部事件,比如按键触发、中断信号等。
    • 支持上升沿、下降沿或双沿触发。
右键

1. Enter User Label(输入用户标签)

  • 功能
    允许用户为当前引脚设置一个自定义标签,用于标识引脚的用途或功能。

  • 特点

    • 设置的标签可以帮助开发者快速识别引脚用途,比如将标签命名为 “LED” 或 “Button”。

    • 标签仅用于代码生成中的备注或提示,对实际硬件配置没有直接影响。

    • 在生成代码后,标签会在代码中体现,比如 #define LED_Pin GPIO_PIN_7 等。


2. Signal Unpinning(取消引脚分配)

  • 功能
    将当前引脚从某个功能模块中解除绑定。

  • 特点

    • 如果引脚已经被配置为某个功能(如 GPIO、ADC、UART 等),选择此选项可以解除该功能分配。

    • 解除绑定后,引脚会重新变为未配置状态(通常是复位状态)。

    • 适用于需要重新配置引脚或者取消不需要的功能分配。


3. Pin Stacking(引脚堆叠)

  • 功能
    允许多个信号或功能共享同一个引脚。

  • 特点

    • STM32 的部分引脚支持多功能,比如一个引脚既可以配置为 ADC,又可以作为 GPIO 使用。

    • Pin Stacking 功能可以在引脚上叠加多个功能,但需要注意优先级和实际使用场景。

    • 适用于复杂应用场景,比如某些信号需要通过软件切换引脚功能。

这里我们想控制PA7所以选择对应的引脚给他设置成output之后再给他设置为高电平就可以点亮了

  • 有别针图标(带着别针)
    当引脚上显示别针图标时,表示这个引脚已经被绑定到一个功能或信号,例如 GPIO、ADC、UART 等。这意味着该引脚的功能已经被固定了,不会处于“未分配状态”。

后面我们在GPIO里面设置我们的PA7引脚,于是有下面的界面

其中

  1. GPIO Output Level(GPIO输出电平)

    • Low(低电平):当配置为低电平时,PA7引脚输出逻辑电平为0(通常接地)。
    • High(高电平):当配置为高电平时,PA7引脚输出逻辑电平为1(通常接电源)。
  2. GPIO Mode(GPIO模式)

    • Output Push Pull(推挽输出)
      推挽模式是双向驱动方式,当输出高电平时,内部电路通过一个上拉晶体管连接到电源;当输出低电平时,内部电路通过一个下拉晶体管连接到地。这种模式适合驱动负载,比如LED灯或其他数字电路。
    • Output Open Drain(开漏输出)
      开漏模式只允许连接到地(低电平),而高电平通常需要外部上拉电阻来实现。这种模式常用于与其他设备通信,比如I²C总线。
  3. GPIO Pull-up/Pull-down(GPIO上下拉)

    • Pull-up(上拉):上拉电阻将引脚拉到高电平,避免引脚在悬空状态下处于不确定状态。
    • Pull-down(下拉):下拉电阻将引脚拉到低电平,也用于避免悬空状态下电平不确定。
    • No Pull(无上下拉):不启用上下拉电阻,通常用于引脚已经通过外部电路确定信号状态的情况。
  4. Maximum Output Speed(最大输出速度)

    • Low(低速):适用于低速信号传输,减少功耗和电磁干扰。
    • Medium(中速):适用于中等速度的信号传输。
    • High(高速):适用于高速信号传输,但可能会增加功耗和电磁干扰。v

这个时候Ctrl + S会生成代码

是否要跳转到C/C++界面

跳转看到的这个文件的列表

1,hello是工程名称,是工程的根目录,包含所有的代码和配置文件

2,Includes是包含的文件夹,里面通常会有一些包含的头文件.h文件,定义了函数,宏定义,数据结构等等

3,Core 核心文件夹,里面包括一些核心的关键代码,比如启动文件startup files,主函数main.c,以及系统初始化的相关文件

4,Drivers驱动文件夹,里面包括和硬件相关的驱动程序,比如GPIO ,USART,SPI等外设的控制代码,我们的STM32里面的HAL或者LL库会生成基本的驱动代码,你也可以在里面添加自定义的驱动

5,hello.ioc是Cube MX配置文件,里面包含一些硬件配置比如引脚功能,时钟设置,外设初始化等等,你可以通过CubeMX等图形界面来改这个文件从而生成代码。

6,STMF103C8TX_FLSH.Id(链接脚本文件),这个是用于指定程序在储存器中的布局,比如代码段,数据段,堆栈等等存放的位置,该文件根据STM32F103C8这个芯片定义了FLash和RAM的地址范围,确保了程序正确加载到储存器

这里我们编译导入板子就可以实现功能了,我们点击的是右上角那儿个样子的绿色的开始按钮,在第一次开始的时候我们选择鼠标的位置,开启自动编译,后面就不会再跳出这个界面了

代码讲解

 int main(void)

  • 主函数是程序的入口,所有的操作和功能从这里开始执行。


2. /* USER CODE BEGIN 1 */ 和 /* USER CODE END 1 */

  • 这些是用户代码区,开发者可以在这些区域添加自己的代码,比如定义变量、初始化外设或者其他功能。

  • 这里没有内容,表示目前没有添加用户代码。


3. HAL_Init()

  • HAL 是 STM32 的硬件抽象层库,HAL_Init() 会初始化 HAL 库,设置一些基本的硬件参数,比如 Flash 接口和 SysTick(系统滴答定时器)

  • 它确保 STM32 的外设可以正常工作。


4. SystemClock_Config()

  • 这个函数用来配置系统时钟,比如时钟源、时钟频率等。

  • 时钟是微控制器运行的核心,影响外设的工作频率以及程序的执行速度。

  • 通常这个函数由 STM32CubeMX 自动生成,根据用户的时钟设置生成对应的代码。


5. MX_GPIO_Init()

  • 这个函数初始化 GPIO(通用输入输出),配置引脚的功能,比如输入、输出、推挽、开漏、上下拉等。

  • GPIO 是微控制器和外部硬件交互的重要接口,比如控制 LED、读取按键状态等。

  • 这个函数也是由 STM32CubeMX 自动生成的。


6. /* USER CODE BEGIN 2 */ 和 /* USER CODE END 2 */

  • 这是另一个用户代码区,通常用来添加初始化的功能,比如设置外设参数、启动定时器等。

  • 这个区域的代码会在主循环开始前执行。


7. while (1)

  • 这是一个无限循环,表示程序在这里会一直运行,直到外部干预(比如复位或电源关闭)。

  • 主程序的逻辑通常放在这个循环内,比如实时读取传感器数据、控制设备等。


8. /* USER CODE BEGIN WHILE */ 和 /* USER CODE END WHILE */

  • 这些是用户代码区,可以在无限循环内添加自己的代码,比如实现具体的功能。


9. /* USER CODE BEGIN 3 */ 和 /* USER CODE END 3 */

  • 这是另一段用户代码区,通常用来添加循环内的功能代码,比如周期性任务、状态机等。

2,让小灯绚丽起来

我们的刚开始那里设置了GPIO output 输出是low

我们在code2那个可以的代码里面那里设置一下,这里设置是高电平

由于我们的cubeide里面已经默认设置了low这里我们设置high的话它会变高电平,所以小灯会亮

如果我们想让小灯闪烁的话,我们尝试再while中设置高低电平切换

这个时候肉眼无法分辨他的快速切换,所以展现还是亮

这个时候我们加一个延时,就会看到闪烁的小灯了

如图我们设置三个引脚控制

这样写一下就可以控制绚丽的小灯了(这里是在上面的code2里面写了state=0)

(这里因为有引脚的标签,我们使用LED_RED_GPIO_Port 代替上面的额GPIOA端口,用LED_RED_Pin来代替上面的GPIO_PIN_7引脚)

3,按键控制

key1

如图模块我们可以知道这样的一个开关采用上拉电压输入模式

(我们认为PB12那里有一个巨大的电阻,如果不摁下的话R11的压降很小这个时候他的电压读取到的PB12就是高电平,所以它摁键摁下的时候就是会有一个约等于0V的电压输入)

这里给设置input

给他一个标签为

在函数界面摁住ctrl+鼠标点击界面就可以跳转到对应的地方

我们的HAL_GPIO_ReadPin这里是一个枚举类型

也就是可以读取到摁键的高低电平输出GPIO_PIN_SET或者RESET

如图我们摁下摁键(摁下就是低电平也就是RESET),小灯的时候就会是亮,松开就会是灭

key2

如果我们没有上拉电阻的话需要通过CubeIDE来设置一个上拉电阻

就在如图所示那个一般默认为浮空输入(无上下拉输入)

这个时候我们调一下就可以了

实现如图功能的时候我们的TogglePin那里后面加了个等待时间,是为了防止反转多次

有些摁键没有硬件消抖我们可以设置软件消抖,如图加一个10ms的延时就可以了

4,GPIO讲解

我们的CubeIDE选择芯片的时候可以去查看我们的芯片手册,如图右上角的Datash

控制单片机就是控制寄存器

如图MCU的IO口的原理图

输入是右边的IO引脚给我的内部芯片,输出是内部给我的IO口(输入上面的两个开关就是用来构成电路用的,如果上拉打开就是我们外面IO没接东西也会是高电平在那里,同样下拉,如果什么都不开的话就是浮空输入也就是高阻态)

如图右边是两个保护二极管

施密特触发器进行波形变成高低电平,,输入的时候会输入模拟信号我们通过ADC来转换成数字信号

下面两个mos管其实是用mcu来放大控制IO引脚的输出电平高低

5,中断

这里给摁键设置为GPIO_EXTI12也就是12号外部中断线(EXTI就是外部中断的意思)

  • External Interrupt Mode with Rising edge trigger detection
    外部中断模式,检测上升沿(电平从低变高)触发中断。
  • External Interrupt Mode with Falling edge trigger detection
    外部中断模式,检测下降沿(电平从高变低)触发中断。
  • External Interrupt Mode with Rising/Falling edge trigger detection
    外部中断模式,上升沿和下降沿都能触发中断。
  • External Event Mode with Rising edge trigger detection
    外部事件模式,检测上升沿触发一个事件,不一定进入中断服务程序(ISR),而是用于某些特殊功能。
  • External Event Mode with Falling edge trigger detection
    外部事件模式,检测下降沿触发事件。
  • External Event Mode with Rising/Falling edge trigger detection
    外部事件模式,上升沿和下降沿都能触发事件。

如图我们摁下摁键的时候会从高电平到低电平,所以就是下降沿触发

之后我们还需要在中断控制器里面勾选上EXIT line{15:10}interrupts

插一个知识点{

(我们这里的中断屏蔽寄存器已经在CubeIDE里面设置了,你的请求挂起寄存器就是上面选择的上升沿或者下降沿触发的那个地方,如果有相应的触发就会让请求挂起寄存器为1)

这里就已经设置了,

之后我们生成代码之后,找到stm32f1xx_it.c文件里面(it后缀就是表明他跟中断interrupt中断有关)

在下面有这个自动生成的中断触发处理函数(如图里面的哪个Toggle反转亮灭的代码)

(下面那个HAL_GPIO_EXTI_IRQHandler(KEY1_Pin)是自动生成的使用来自动清除请求挂起寄存器用的,为了防止中断反复运行,让他一次只中断一次)

如图所示正常情况下绿色部分会while死循环进行,如果有摁键摁下那么就会进行EXTI15_10_IRQHandler,也就会运行中断函数了,如果运行完这个 了那么就会回到while循环

这里要注意设置一个软件消抖功能,也就是在原来的基础上加一个等待10ms,因为你无法判断是摁下的低电平还是抖动的低电平

这里我们发现崩了,因为我们的HAL_delay要依赖一个System tick timer 系统滴答的中断可以为其提供一个1ms的时钟基准,但是这个中断的优先级比我们现在用的这个触发的中断的优先级要低,所以不能正常运行,所以要去调一下优先级

这里我们给System tick timer 设置14优先级,给interrupts中断设置15优先级

优先级越高数字就越小,这个时候就可以运行那个中断了

数字越小优先级越高

我们可以在自由选择几位作为抢占优先级(老大哥),几位作为响应优先级,

比较如图,同时发生的时候两个都比较

如果一个正在发生就只比较抢占,抢占比较大才能插队

6,深入学习中断

USB全称Universal Serial Bus 通用串行总线

TTL是两根线就可以通信,TX连接RX,有的时候要加一个GND作为参考点

用到串口了所以这个SYS就要设置Seial Wire了

USART:Universal Synchronous/Asynchronous Receiver & Transmitter通用同步或异步接收器和发送器(同步就是两人,一人在发送另外一人在等你发送完,他就干等着,异步就是你在发送,它可以去干别的事情)

TTL用的就是UART(异步)

我们的type-c接的是PA2和PA3,里面可以看到PA2可以设置为TX

这里我们给USART2里面模式设置为Asynchronous异步模式,于是右边芯片处就已经被设置了

这里我们左边可以看到通用异步串口的基本参数

(看下面这些的时候我们可以先把上面的图截屏放在一侧来看)

Basic Parameters(基本参数)

  1. Baud Rate (波特率)

    • 图里设置的是 115200 Bits/s

    • 它表示数据传输的速率,也就是 每秒钟传输多少比特

    • 常见的有:9600、19200、38400、115200 等。

    • 注意:发送端和接收端必须波特率一致才能正常通信。

  2. Word Length (数据位/字长)

    • 图里是 8 Bits (including Parity)

    • 代表一帧数据里有效数据的位数。常见有 8位9位

    • 如果启用奇偶校验位 (Parity),有些芯片会把它算进这 8 位里。

  3. Parity (校验位)

    • 图里设置的是 None (无校验)

    • 可选项一般有:None(无)Even(偶校验)Odd(奇校验)

    • 作用:用来检测数据是否出错(但不纠错)。

  4. Stop Bits (停止位)

    • 图里设置的是 1

    • 一帧数据的结束标志。

    • 常见取值:1 位、1.5 位、2 位

    • 举例:

      • 1 位停止位:速度快,但容错率低。

      • 2 位停止位:速度慢,但更稳定。


Advanced Parameters(高级参数)

  1. Data Direction (数据方向)

    • 图里是 Receive and Transmit(收发)

    • 表示串口既能接收数据,也能发送数据。

    • 当然有的场合可以只设置为收 (RX) 或只发 (TX)。

  2. Over Sampling (过采样率)

    • 图里是 16 Samples(16 倍过采样)

    • 意思是:在采样时钟比波特率快 16 倍,从而更精准判断数据的 0/1。

    • 一般取值:16 倍(精度高,稳定) 或 8 倍(速度快)

之后我们生成代码,看到函数里面生成了一个MX_USART2_UART_Init()的初始化代码

这里我们设置单片机发送信息:HAL_UART_Transmit,这个时候还是单片机发送给电脑(这里括号里最后那个是100ms倒数第二个是发送的字节长度)

如图波特率什么的都要设置对应

如图设置一个电脑发送信息:HAL_UART_Receive,也就是电脑发送单片机接受

这里的右边的HAL_MAX_DELAY是接收时间无线长的意思

如图给小灯设置标签作为output方便一会儿检验

小灯高电平有效

如图所示默认电平GPIO_PinState state=GPIO_PIN_SET也就是高电平

如果第二位收到信息0就设置为高电平,之后对应小灯也有指令

成功

8,串口中断

上一章我们讲的那些串口的,他们要全部发送或者接收完了才会继续其他的执行,中断模式则可以打破等待

并且有确定的长度接受范围

我们内部的串口轮询模式是这样的:如图CPU会把信息给发送数据寄存器,然后他会给发送移位寄存器,然后发送移位寄存器会进行解码成相应的01,完成之后CPU再发发送下一个信息给发送数据寄存器,直到发送完或者超过时间

接受与之相反

这样的时候CPU会一轮一轮的查询又没有信息可以处理,就无法顾及其他代码了,这个状态我峨我们称之为堵塞

如图可以用串口中断,如果发送移位寄存器还没解码完成也就是信息还占用发送数据寄存器和移位寄存器的话这个时候CPU是会进行其他代码的运行的

这我们打开USART2的中断

如图也就是再原来的HAL_UART_Transmit基础上加了一个IT,这个时候CPU不会在堵塞了,所以不用设置一个超时时间了,如图第三行

Receive不能放在while里面了:

因为开启串口中断之后我们的代码会顺着继续运行下去,所以我们Receive不能放在while里面了,因为你会在还没有发送完成的时候调用串口中断顺着下去,会一直while循环所以会一直接受这个时候就会接受不完全并且每次都接受前几个,并且会无限循环就会乱码(Transmit也不能放在里面了其实,否则会一直发送,当然也可放但是后面要及时的加一个延时,但是我们为的就是少时间,所以后面会拿出来的)

stm32f1xx_it.c里面找到USART2的中断,之后在里面找到回调函数Callback

如图所示我们如果同样给接收设置一个中断的话,还不等他接收完毕他就会继续向下执行,因此我们找到stm32f1xx_it.c里面找到USART2的中断,之后在里面找到回调函数Callback,这个函数的意思就是我们的Receive接受完成之后才会调用的一个函数,之后我们在外面定义它,把我们的发送代码写在里面,当我们的接受完成手就会调用再次发送(就是把这个代码写在Callback里面)

(Callback是接收到指定数据长度才会触发的举例:你用 HAL_UART_Receive_IT(&huartx, buf, len) 接收 len 个字节,只有全部收完 len 个字节,才会进入这个回调。)。

这次接收完成后会开启下次接收

如此就实现了

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐