在上一章中,我讲解了搭建一个机器人电控系统的前期准备工作,包括电源的选择和主控的开发,经过一些基础的单片机任务之后,现在你应该对单片机的一些编程习惯有了掌握。
在本章中,我将会按照一个机器人的底盘到上层机构的顺序,单独来对每一个机构的电控部分进行讲解。从中分别来详细展示运动模型、通信协议、PWM输出和算法部分,从而让你学会如何用MCU去控制每个部分机构的运动。
不过在这之前,我会以STM32F407为例,先补充一些关于MCU的基本知识原理:
STM32系统架构
STM32时钟配置
GPIO原理
中断NVIC与EXTI
在画一块主控的讲解中,曾经讲解过一个晶振电路。晶振好比单片机的心脏,如果没有心脏起跳,单片机也将无法在正常工作,单片机收到一个脉冲,就执行一次或多次指令。
而单片机时钟的来源就是晶振。单片机的时钟是让单片机各个部分能够“按规律”有序运行的核心,时钟的频率决定了单片机运行的速度。所以了解时钟作用在单片机上的原理是明白很多配置语句的关键所在。
在第二章节讲如何画主控板的最小原理图时,我们曾讲过晶振电路来源,当时我们的主控板接了两个外部晶振:
第一个接在PH0-OSC_IN和PH1-OSC_OUT之间大小为8MHZ的晶振,现在我们把它称为HSE(High Speed External Clock signal)高速外部时钟信号。其实在单片机内部本身还存在一个可用的高速时钟信号,我们把它叫做HSI(High Speed External Clock signal)高速内部时钟信号。
第二个接在PC14-OSC32_IN和PC15-OSC32_OUT之间大小为32.768KHZ的晶振,是为了提供精准定时用的(因为32768=2^15)。现在我们把它叫做LSE(Low Speed External Clock signal)低速外部时钟信号。同样在单片机内本身还存在一个可用的低速时钟信号,我们把它叫做LSI(Low Speed External Clock signal)低速内部时钟信号。
再查看STM32F4参考手册中的时钟树,现在来看就很清晰了。我们把时钟树分成两半来看,左边的那条主干就是时钟信号的输入;而右边讲的是时钟信号到了哪里,也就是什么地方需要时钟信号,需要多大的时钟信号:
左端时钟信号输入线:
右端时钟信号到达:
①独立看门狗时钟和实时时钟RTC
独立看门狗时钟:来自于LSI低速内部时钟(32KHZ)。
实时时钟RTC:来自于LSI(32KHZ)、LSE(32.768KHZ)、HSE(分频2到31)。通过实时时钟RTCSEL进行选择。
②系统时钟SYSCLK——提供包括外设总线AHB和以太网PTP时钟
来自于HSI(16MHZ)、HSE(4—26MHZ与原理图相关)、PLLCLK(通过PLL分频器分频的时钟)。通过SW进行选择。
*PLL(Phase Locked Loop):锁相回路/锁相环,用于统一整合时钟信号,使高频器件正常工作。其中包括了倍频器(N)和分频器(P、Q、R)。
*PLLCLK形成过程:选择HSI或HSE先进行分频(/M),再经过PLL锁相环整合得到PLLCLK。
③外设专用48MHZ时钟
单独由PLL得到48MHZ的PLL48CLK时钟。
④I2S音频时钟
来自外部输入音频信号I2S_CKIN、PLLI2SCLK(HSI或HSE通过PLL锁相环得到)。通过I2SSRC进行选择。
⑤以太网时钟和USB时钟
来自于外部以太网输入信号和外部USB输入信号。
外设总线AHB:
在整个时钟树中,我们需要重点关注外设总线AHB的时钟来源和去向,这和下面我们要进行时钟的软件配置有关。
来源:系统时钟SYSCLK(最高168MHZ),通过AHB预分频器(1到512偶数倍分频)。
去向:
①提供AHB总线时钟信号HCLK。
②提供Cortex系统定时器SysTick(专门用于程序延时)。
③提供系统自由运行时钟FCLK(这个就是单片机的主频)。
④提供APB总线时钟PCLK。(分为外设APB时钟和定时器APB时钟两大方面,通过APB预分频器【1到16偶数倍分频】分频得到。定时器APB时钟在APB预分频器不为1的情况下需要倍频*2。这也是为什么在PWM计算占空比的时候主频要取那个值)
以上,这就是为什么我们在编所有程序的时候,第一就是要使能那个功能对应的时钟,只有时钟信号到达了,整个功能才能开始工作。
现在让我们来看一个串口时钟配置的例子:
HSI、HSE、PLLCLK通过SW进行选择后得到SYSCLK,再得到AHB线时钟和APB线时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
外设USART1挂载在APB2总线上,所以先要对APB2总线时钟进行使能,让时钟信号能够到达外设USART1。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
外设GPIOA挂载在AHB1上,所以先对AHB1总线时钟进行使能,让时钟信号能够到达外设GPIOA。
在之前,我们讲了MCU的时钟硬件来源和时钟树以及串口时钟的例子,你应该已经大致弄懂了单片机的时钟是如何工作的,流向是如何的以及它的重要性。
我们讲了很多选择器,选择哪个是不确定的。很多分频器,分频系数是不确定的。但是你会发现在我们串口使能并没有选择这些参数,仅仅是使能打开了那个通道。事实上,这些参数都在你开发一个主控导入的那一系列启动文件中所定义了。所以在本节中,将会介绍软件上是如何决定这些参数的。
①参数一:单片机选择了哪个时钟源?
system_stm32f4xx.c文件:
首先要知道这个这个文件是干嘛用的,其实每个文件开始都有英文备注在提醒你:
由说明备注知道,这个文件有两个函数:
①SystemInit():设置系统时钟函数,包括:
②SystemCoreClockUpdate():用于更新变量SystemCoreClock ,这个变量的赋值是HCLK或者说主频,这个变量被用于去实现Cortex内核自带的那个定时器,用于程序延时。
整个文件的工作过程:
单片机上电复位/或按键复位后,程序先执行startup_stm32f4xx.s启动函数,启动函数中默认选择HSI(16MHZ)作为时钟源。system_stm32f4xx.c在startup_stm32f4xx.s中被调用,执行其中的SystemInit()函数,而在这个函数中除了复位一些寄存器的初始位置外,经过了一个叫做SetSysClock()的函数:
这个函数上方的关键注释是:Configure the System clock source(初始化时钟源)
这个函数的说明是:PLL (clocked by HSE) used as System clock source,即将HSE作为时钟源经过PLL锁相环得到系统时钟。
由此我们得出结论:在MCU的启动过程中,先默认把HSI(16MHZ)作为时钟源启动,然后再经过SystemInit()中的SetSysClock()函数,将HSE作为时钟源经过PLL锁相环后的时钟作为真正的系统时钟!
②参数二:分频系数是多少?
现在我们知道了单片机选择的时钟的具体路线,现在关键就是如何确定路线上的分频系数,这些分频系数包括:分频系数M,倍频系数N,分频系数P,如下图。
同样在system_stm32f4xx.c文件中,有对这几个系数变量的初始化:
根据公式计算:系统时钟SYSCLK=8MHZ*N/M/P=168MHZ,即SYSCLK的最大频率:
以上,解决了SYSCLK的问题。
AHB,APB的分频系数是多少?
其实在SystemInit()中的SetSysClock()函数中已经做了初始化:
以上是分频系数的选择,这是初始化默认的分频,当然你可以改成其他分频。
其实在system_stm32f4xx.c开头的表格中已经告诉过我们所有参数的默认值:
但是当你需要移植到另外一个型号的板子的时候,你需要知道怎么改才能移植,如果427和407的主频是不一样的,你应该怎么改?
③时钟线到了哪?——stm32f4xx_rcc.c文件
经过以上的分析,我们得出:
在stm32f4xx_rcc.c文件中,对不同时钟线上所挂载的设备进行了初始化,通过该文件可以知道:
为什么使能是这样写的?