STM32应用开发实践教程:能小车循迹状态获取的应用开发

2023-10-30 20:40

本文主要是介绍STM32应用开发实践教程:能小车循迹状态获取的应用开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

3.1.1 任务分析
本任务要求设计一个应用程序,以实现周期性地获取智能小车循迹的状态。任务要求使用反
射式红外光电传感器电路板作为智能小车的循迹模块,实现智能小车的巡线前进功能。反射式红
外光电传感器和循迹电路板如图 3-1-1 所示。

反射式光电传感器的光源有多种,常用的有可见光、红外光和激光,本任务选取红外光源。
单个反射式红外光电传感器自带一个红外光源和一个光接收装置,光源发出的光经待测物体反射
后被光接收装置(光敏元件)接收,再经过信号调理电路处理即可获得所需信息,一般为数字信
号“1”或“0”。利用传感器的这个特性可以检测地面明暗程度和颜色的变化,也可以探测有无
接近的物体。
根据上述反射式红外光电传感器的工作原理来分析此任务,并在智能小车底盘前部安装反射
式红外光电传感器电路板(循迹电路板),板上装有 8 个反射式红外光电传感器。智能小车循迹
的具体工作原理如下:
 红外发射管发射光线到路面,光线遇到白底后被反射,接收管收到反射光,经过信号调
理电路处理后输出高电平;
 光线遇到黑底后被吸收,接收管没有收到反射光,经过信号调理电路处理后输出低电平;
 循迹电路板共输出 8 路数字信号到智能小车的主控板,其中数字信号“1”表示白色路面,
数字信号“0”表示黑色路面;
 智能小车主控板将接收到的 8 路数字信号作为车身当前状态的判别依据,并进一步控制电
机转动以使车身归位。测试场地路面有黑、白两色,其中黑色路面为循
迹线,宽 30mm,智能小车可循此线前进。测试场地示意如图 3-1-2 所示。


综上所述,如果要求智能小车以一定的速度沿着 黑色跑道前进,则微控制器需要以较短的周期频繁地获取循迹电路板上的 8 路数字信号,进而才
能通过及时判断车身状态以调整电机的转向。智能小车的该要求可通过定时器的基本定时功能来
实现,因此本任务涉及的知识点有:
 STM32F4 系列微控制器定时器的基本功能特性;
 STM32F4 系列微控制器定时器的基本定时功能的编程配置方法。


3.1.2 知识链接
1.STM32F4 系列微控制器定时器概述
STM32F4 系列微控制器共有 14 个定时器,编号为 TIM1~TIM14,其中包括 2 个高级控制定
时器、10 个通用定时器和 2 个基本定时器。
上述 3 种类型的定时器中,基本定时器功能最少,只有基本的定时功能和驱动数模转换器
(Digital to Analog Converter,DAC)的功能,不具备外部通道。通用定时器和高级控制定时器
的功能较强,如具有独立的外部通道,可用于输入捕获、输出比较、脉宽调制(Pulse Width
Modulation,PWM)信号输出等,支持正交编码器与霍尔传感器等电路。表 3-1-1 对各定时器
的功能特性进行了总结概括,查看此表时要注意区分 3 种类型定时器的功能差异

 

 表 3-1-1 仅列出了各类定时器存在差异的指标,参数相同的指标并未列出。

另外,定时器时钟(TIMxCLK)频率与 PCLKx 的关系如下。
如果 APB 预分频器(RCC_CFGR 中的 PPRE1、PPRE2)的分频系数配置为 1,则 TIMxCLK=
PCLKx;否则,定时器时钟频率将被设置为与定时器相连的 APB 域的频率的两倍。定时器时钟
频率与 PCLKx 的关系如图 3-1-3 中蓝色阴影和红色方框部分所示。

 

编者使用的 STM32F4 开发板上的微控制器型号为 STM32F407ZGT6,在任 务 1.3 的学习中,
我们配置 HCLK 为 168 MHz,PCLK1 为 42 MHz,PCLK2 为 84 MHz,即 APB1 和 APB2 的分
频系数分别为 4 和 2。根据上述关系可知,定时器时钟频率 TIMxCLK = 2 × PCLKx。以 TIM13
为例,若其连接外设总线 APB1 = 42 MHz,那么 TIMxCLK = 2 × 42 MHz= 84 MHz。
对于STM32F42xxx 和STM32F43xxx 系列微控制器而言,根据《STM32F4xx 中文参考手册》,
定时器时钟预分频器由 RCC 专用时钟配置寄存器(RCC_DCKCFGR)的“TIMPRE”位段进行
配置(STM32F40xxx 系列微控制器无此寄存器),该寄存器各位的定义如图 3-1-4 所示。

 从图 3-1-4 中可以看到,RCC 专用时钟配置寄存器只有第 24 位“TIMPRE”有效,它被称
为“定时器时钟预分频器选择位”。该位默认值为“0”,TIMxCLK 频率的配置情况如前所述。
当该位被配置为“1”时,如果 APB 预分频器(RCC_CFGR 中的 PPRE1、PPRE2)的分
频系数配置为 1、2 或 4,则 TIMxCLK = HCLK;否则,定时器时钟频率将被设置为与定时器相
连的 APB 域的频率的 4 倍:TIMxCLK = 4 x PCLKx。以 TIM13 为例,APB1 外设总线的分频系
数为 4,因此 TIMxCLK =HCLK= 168 MHz。
2.基本定时器的功能框图解析
根据 3.1.1 节的任务分析结果可知,本任务需要用到定时器的基本定时功能,因此选择
STM32F4 系列微控制器的基本定时器即可。通过基本定时器的功能框图来学习其各部分的功能
特性,其功能框图如图 3-1-5 所示。

从图 3-1-5 中可以看到,基本定时器包含 3 部分:时钟源、控制器模块和时基单元。
自动重载寄存器和预分频器(PSC)的长方形框带有阴影,代表这两个寄存器带有影子寄
存器。
自动重载寄存器左侧有一个“事件”标志,表示在更新事件发生时,使用预装载值更新自动
重载影子寄存器。其右侧的“事件”标志与“中断和 DMA 输出”标志表示当计数器寄存器值与
自动重载寄存器值相等时,将发生事件、中断和 DMA 输出。
下面对基本定时器的各部分组成分别进行介绍。
(1)时钟源
功能框图中非常明确地表示了基本定时器的时钟源只能来自内部时钟(CK_INT),即 RCC
的 TIMxCLK。关于 TIMxCLK 频率的具体计算方法已在前述内容中阐述了,此处不再赘述。
通用定时器与高级控制定时器的时钟源除了可来自内部时钟,还可来自外部时钟或者其他定
时器等。
(2)控制器模块
基本定时器的控制器模块用于控制定时器的复位、使能与计数,或者用于触发 DAC 的转换
使能等。
(3)时基单元
基本定时器的时基单元由一个 16 位递增计数器(图 3-1-5 中的 CNT 计数器)及其相关的
自动重载寄存器组成,计数器的时钟通过预分频器进行分频。计数器寄存器、预分频器寄存器和
自动重载寄存器可通过软件进行读写。即使在计数器运行时也可对它们执行读写操作。
时基单元包括以下 3 部分。

① 计数器寄存器
计数器寄存器(TIMx_CNT)中存储了定时器当前的计数值。
② 预分频器寄存器
从图 3-1-5 可以看到,预分频器的输入为 CK_PSC(等于 CK_INT),经分频后,输出为
CK_CNT,分频系数由 16 位预分频器寄存器(TIMx_PSC)中的值决定,介于 1~65536。CK_CNT
的时钟频率与 CK_PSC 的时钟频率关系如下。
f CK_CNT =  f CK_PSC  / (TIMx_PSC + 1)
计数器由 CK_CNT 提供时钟,预分频器寄存器由于有缓冲,因此可被实时更改,但新的分
频系数将在下一个更新事件发生时被采用。图 3-1-6 展示了预分频器的分频系数由 1 变为 2 时
的计数器时序图。

 

从图 3-1-6 中可以看到,计数器计数到“F8”时,在 TIMx_PSC 中写入了新值“1”(图
中蓝色阴影处,原值为“0”)。在此之前, f CK_CNT =  f CK_PSC  。但写入新值后,预分频器的分频系
数并没有马上变为 2,在更新事件发生时(图中橙色阴影处), f CK_CNT 的频率才变为 f CK_PSC 的 2
分频。
③ 自动重载寄存器
自动重载寄存器(TIMx_ARR)由两部分构成:预装载寄存器和影子寄存器。真正起作用
的是影子寄存器,它支持预装载,每次尝试对它执行读写操作时都会访问预装载寄存器。预
装载寄存器的内容既可以直接传输到影子寄存器,也可以在每次发生更新事件(UEV)的时
候传输到影子寄存器,这取决于 TIMx_CR1 中的自动重载预装载使能位(APRE),其工作特
性如下。
 当 APRE=0 时,TIMx_ARR 不进行缓冲,预装载寄存器的值直接传到影子寄存器中,如
图 3-1-7 所示。
 当 APRE=1 时,TIMx_ARR 预装载(进行缓冲),当发生更新事件后,预装载寄存器的
值才传输到影子寄存器中,如图 3-1-8 所示。

 

对时基单元的计数过程总结如下。
 计数器从 0 开始计数,当其值变为自动重载值(TIMx_ARR 的值)时,发生“计数器
上溢”。
 每次发生计数器上溢时会生成“更新事件(UEV)”,同时更新所有寄存器并将“更新中
断标志位”(TIMx_SR 中的 UIF 位)置 1。
 更新所有寄存器的动作,具体包括:
 使用预装载值(TIMx_PSC 的内容)重新装载预分频器的缓冲区;
 使用预装载值(TIMx_ARR 的内容)更新自动重载影子寄存器。 

计数器重新从 0 开始计数。
下面通过一张计数器工作时序示意图(如图 3-1-9 所示)来说明上述计数过程,在该示意
图中 TIMx_PSC 的值为 1(即 2 分频 ),TIMx_ARR 的值为 36。

.定时器基本初始化结构体介绍
STM32F4 标准外设库为定时器外设提供了 4 个初始化结构体,本任务由于只用到定时器中
断功能,因此只涉及定时器基本初始化结构体(TIM_TimeBaseInitTypeDef)。该结构体用于配
置定时器的基本工作参数(如预分频器分频系数、计数模式和定时器周期等),被
TIM_TimeBaseInit()函数调用。其原型定义如下所示:

typedef struct {
uint16_t TIM_Prescaler; // 预分频器
uint16_t TIM_CounterMode;  // 计数模式
uint32_t TIM_Period;  // 定时器周期
uint16_t TIM_ClockDivision; // 时钟分频
uint8_t TIM_RepetitionCounter; // 重复计数器
} TIM_TimeBaseInitTypeDef;

 对结构体各成员变量的作用介绍如下。
(1)TIM_Prescaler
定时器预分频器分频系数配置,它被用于配置预分频器寄存器的值,可配置的范围为
0~65535,对应 1~65536 分频。
(2)TIM_CounterMode
定时器计数方式配置,可配置的参数如下:
 向上计数(TIM_CounterMode_Up);
 向下计数(TIM_CounterMode_Down);
 中心对齐模式 1(TIM_CounterMode_CenterAligned1);
 中心对齐模式 2(TIM_CounterMode_CenterAligned2);
 中心对齐模式 3(TIM_CounterMode_CenterAligned3)。
对于基本定时器而言,只能使用向上计数的方式,因此无须配置该项,使用默认值即可。

(3)TIM_Period
定时器周期配置,实际上本成员变量配置的是自动重载寄存器的值,事件生成时更新到影子
寄存器,可配置的范围为 0~65535。
(4)TIM_ClockDivision
时钟分频配置,它被用于配置定时器内部时钟频率与数字滤波器采样时钟频率的分频比,基
本定时器不具备输入捕获功能,可不用进行时钟分频配置。
(5)TIM_RepetitionCounter
重复计数器配置,高级控制定时器专用,基本定时器可不用进行该配置。
4.定时器中断功能的编程配置步骤
掌握了基本定时器的功能特性与初始化结构体的配置内容后,我们可进行完整的定时器中断
功能编程配置的学习。接下来我们将学习如何配置定时器 6,使之按时产生中断,并在中断服务
函数中完成相应的工作。具体的编程配置步骤如下。
(1)开启 TIM6 时钟
根据表 3-1-1 可知,TIM6 挂载在 APB1 上,因此需要调用 ABP1 的时钟使能函数以开启
TIM6 的时钟。具体代码如下:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);  // 使能 TIM6 时钟
(2)配置定时器的工作参数
首先配置“定时器基本初始化结构体(TIM_TimeBaseInitTypeDef)”的各成员变量,然后调
用 TIM_TimeBaseInit()函数以完成参数的初始化。
我们通过一个实例来学习具体的配置方法。如在实际应用中,要求每隔 1s 采集一次环境温
湿度信息,使用定时器 6 实现,该如何配置定时器?
在上述实例中,我们可先配置 CK_CNT 频率。TIM6 挂载在 APB1 上,定时器时钟源频率
(CK_INT = CK_PSC)为 42 MHz×2=84 MHz。可将 TIMx_PSC 配置为 8399,根据计算公式可得:
f CK_CNT = 84 MHz/ (8399 + 1) = 10000 Hz(周期为 100μs) 
配置 TIMx_ARR 的值为 9999,进而即可将定时器配置为每隔 1s 产生更新中断。间隔时间
的计算方法如下:

100 μs×(TIMx_ARR + 1) = 1000000 μs = 1s 
实现上述配置要求的代码块如下:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);
TIM_TimeBaseStructure.TIM_Period = 10000-1;  // 配置 TIMx_ARR
TIM_TimeBaseStructure.TIM_Prescaler = 8400-1; // 配置 TIMx_PSC
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseStructure);

另外可根据以下公式计算定时器的溢出时间( T out )。
T out (μs) = [(TIMx_ARR + 1)×(TIMx_PSC + 1)]÷ f CK_PSC (MHz) 
在上述公式中, T out 的单位为 μs, f CK_PSC 为定时器的工作频率,单位为 MHz。将实例中的各
个数字代入上式后可得:
1s = 1000000 μs =(10000×8400)÷84(MHz) 
(3)配置允许定时器产生更新中断
本任务要求在到达定时时间后,产生更新事件并将中断标志位置 1,因此需要配置允许 TIM6
产生更新中断。STM32F4 标准外设库使用 TIM_ITConfig()函数来进行定时器中断使能,它的函
数原型定义如下:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
第一个参数为定时器编号,取值为 TIM1~TIM14。
第二个参数用于指明要使能的定时器中断的类型,在本任务中使用更新中断,因此要配置为
“TIM_IT_Update”。
第三个参数指明使能(ENABLE)或失能(DISABLE)。
例如要使能 TIM6 的更新中断,具体代码如下:
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
(4)配置定时器中断优先级
与任务 2.2 中的“按键中断”优先级和任务 2.3 中的“串口接收中断”优先级配置类似,使
能定时器的“更新中断”后,也须对 NVIC 进行优先级配置。使用以下代码可配置 TIM6 的中断
优先级。 

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* TIM6 NVIC 配置 */
NVIC_InitStructure.NVIC_IRQChannel = TIM6_DAC_IRQHandler;  // 定时器 6 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; // 抢占优先级 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; // 子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

(5)使能定时器
完成定时器的相关配置以后,须开启定时器才能使其开始工作。这里通过将控制寄存器
(TIMx_CR1)的“CEN”位段置 1 实现。STM32F4 标准外设库通过调用 TIM_Cmd()函数实现,
该函数的原型定义如下:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
使用实例如下:
TIM_Cmd(TIM6, ENABLE);
(6)编写定时器中断服务函数
定时器中断服务函数用于处理中断发生后的事件。定时器支持多种中断类型,但中断的入口
函数一般是统一的,如 TIM6 的中断服务函数为“TIM6_DAC_IRQHandler()”。因此在进入中断
服务函数后,首先应通过 TIMx_SR 判断中断类型,然后再执行相应的后续操作。STM32F4 标准
外设库提供了获取中断类型的函数,它的函数原型定义如下:
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
该函数的作用是:判断当前定时器发生了哪种类型的中断。具体使用实例如下:
if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET){};
上述语句用于判断当前 TIM6 是否发生了“更新中断(TIM_IT_Update)”。
另外,处理完中断之后,应将相应的中断标志位清除,即向 TIMx_SR 的相应位写入 0。
STM32F4 标准外设库提供了清除中断标志位的函数,函数原型定义如下:

void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
该函数的作用是:清除定时器相应的中断标志位。具体使用实例如下:
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
上述语句用于清除 TIM6 的“更新中断标志位”。
下面给出定时器中断服务函数的一般框架,其中入口函数名需要根据实际定时器进行修改,
如定时器 6 的中断入口函数名为“TIM6_DAC_IRQHandler()”。

void TIMx_IRQHandler(void)
{
if(TIM_GetITStatus(TIMx,TIM_IT_Update) == SET) // 如果产生了更新中断
{
/* DoSomething */
}
TIM_ClearITPendingBit(TIMx,TIM_IT_Update); // 清除更新中断的标志位
}

3.1.3 任务实施
1.根据任务要求计算 TIMx_ARR 与 TIMx_PSC 值
3.1.1 节的任务要求智能小车以一定的速度沿着黑色跑道前进,因此微控制器需要以较短的
周期频繁地获取循迹电路板上 8 路光电传感器的实时状态。可将 TIM6 的定时中断时间配置为 10
ms,然后在中断服务函数中获取循迹电路板的状态数据,以作为应用程序控制电机的依据。可
配置定时器的工作参数为 TIMx_PSC =8399,TIMx_ARR = 99。
根据计算公式可得:
f CK_CNT = 84 MHz/ (8399 + 1) = 10000 Hz(周期为 100 μs) 
定时中断时间为:
100 μs×(TIMx_ARR + 1) = 10000 μs = 0.01s = 10 ms 
2.编写 TIM6 定时中断初始化程序
复制一份 task2.3_USART_WaterFlow_LED 工程,并将其重命名为“task3.1_Timer_Interrupt_
GetTrackData”。在“HARDWARE”文件夹下新建“TIMER”子文件夹,新建“timer6.c”和“timer6.h”
两个文件,将它们加入工程中,并配置头文件包含路径。
在“timer6.c”文件中输入以下代码:

#include "timer6.h"
/**
* @brief TIM6 定时中断功能初始化
* @param arr:  自动重装载值, psc:  定时器预分频值
* @retval None
*/
void TIM6_Int_Init(uint16_t arr, uint16_t psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);// 使能 TIM6 时钟
TIM_TimeBaseInitStructure.TIM_Period = arr; // 自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 定时器预分频值
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure); // 初始化 TIM6
TIM_ClearITPendingBit(TIM6, TIM_IT_Update); // 清除更新中断请求位
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); // 允许定时器 6 更新中断
TIM_Cmd(TIM6,ENABLE); // 使能定时器 6
NVIC_InitStructure.NVIC_IRQChannel = TIM6_DAC_IRQn;  // 定时器 6 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;  // 抢占优先级 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; // 子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}在“timer6.h”文件中输入以下代码:
#ifndef __TIMER6_H
#define __TIMER6_H
#include "sys.h"
void TIM6_Int_Init(uint16_t arr, uint16_t psc);
#endif

3.编写循迹电路板连接端口初始化程序
在“HARDWARE”文件夹下新建“TRACK”子文件夹,新建“track.c”和“track.h”两个
文件,将它们加入工程中,并配置头文件包含路径。
在“track.c”文件中输入以下代码:

#include "track.h"
#include "led.h"
#include "usart.h"
/*  存放循迹电路板上传来的 8 位二进制数 */
uint8_t trackData = 0;
uint16_t tCount = 0;
/**
* @brief 循迹电路板端口初始化
* @param None
* @retval None
*/
void TrackBoard_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);// 使能 GPIOF 时钟
/* PF0~PF7 端口初始化设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3 \
|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;  // 输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;  // 引脚速率 100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  // 上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);
}在“track.h”文件中输入以下代码:#ifndef __TRACK_H
#define __TRACK_H
#include "sys.h"void TrackBoard_Init(void); // 循迹电路板端口初始化#endif

4.编写 TIM6 的定时中断服务函数
TIM6 的定时中断服务函数理论上可放置在应用程序中的任意一个源代码文件中,但中断服
务函数中通常包括数据处理程序,因此从编程的便利性来说应将中断服务函数与数据处理程序放
置在同一个源代码文件中。本任务将其编写在“track.c”文件中,具体代码如下:

/**
* @brief TIM6 定时中断服务函数
* @param None
* @retval None
*/
void TIM6_DAC_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6,TIM_IT_Update) == SET)// 若发生更新中断
{
tCount++;
/*  获取 GPIOF 16 bit 数据中的低 8 位 (bit0 ~ bit7) */
trackData = GPIO_ReadInputData(GPIOF) & 0xFF;
if(tCount >= 200)  // 每隔 2s 翻转 LED1 ,打印信息
{
LED1 = ~LED1;
printf("trackData is 0x%x\r\n",trackData);
tCount = 0;
}
}
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);// 清除更新中断标志位
}

5.编写 main()函数
在“main.c”文件中输入以下代码:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer6.h"
#include "track.h"
int main(void)
{
delay_init(168); // 延时函数初始化
LED_Init(); //LED 端口初始化
USART1_Init(115200);  //USART1 初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/*  配置 TIM6 定时中断时间为 10 ms */
TIM6_Int_Init(100-1, 8400-1);
TrackBoard_Init();
printf("System Started...\r\n");
while(1)
{
}
}

6.观察试验现象
本任务中 8 路循迹电路板数据的输出端口与 STM32F4 系列微控制器的 PF0~PF7 引脚相连,
为了更加方便地观察试验现象,已在 TIM6 的定时中断服务函数中将每隔 2s 获取的循迹电路板
数据通过 USART1 发送到上位机。应用程序编译无误后,下载至开发板运行,打开上位机的串
口调试助手,可观察到如图 3-1-10 所示的定时器中断试验现象。
由于循迹电路板端口初始化为输入模式,默认上拉,因此从图 3-1-10 中可以看到默认获取
到的 8 位数据全为“1”,即十六进制 0xff。使用杜邦线将 PF0 端口接地后,获取到的数据变为
0xfd,即最低位被清零。

 

这篇关于STM32应用开发实践教程:能小车循迹状态获取的应用开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/310465

相关文章

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

微信公众号脚本-获取热搜自动新建草稿并发布文章

《微信公众号脚本-获取热搜自动新建草稿并发布文章》本来想写一个自动化发布微信公众号的小绿书的脚本,但是微信公众号官网没有小绿书的接口,那就写一个获取热搜微信普通文章的脚本吧,:本文主要介绍微信公众... 目录介绍思路前期准备环境要求获取接口token获取热搜获取热搜数据下载热搜图片给图片加上标题文字上传图片

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2