在不久前曾研究过最近最为流行的开源飞控Pixhawk的源代码,但是由于在这之前没有接触过操作系统这个概念,在不知道代码执行流程的情况下看了几个星期的时间,脑子里还是一团乱,所以决定还是从裸机开始研究,因此再往上买了一个小的开源四旋翼作为入门。废话不多说,开始进入正题!
按照之前看裸机程序的思路肯定先找到main文件来一个一个往下看,在这里把main函数先贴出来:
int main(void)
{
SystemClock_HSE(9); //系统时钟初始化,时钟源外部晶振HSEs 8*9=72MHz;
cycleCounterInit(); // Init cycle counter
SysTick_Config(SystemCoreClock / 1000); //SysTick开启系统tick定时器并初始化其中断,1ms
UART1_init(SysClock,BT_BAUD_Set); //串口1初始化
NVIC_INIT(); //中断初始化
STMFLASH_Unlock(); //内部flash解锁
LoadParamsFromEEPROM();
LedInit(); //IO初始化
BT_PowerInit(); //蓝牙电源初始化完成,默认关闭
MotorInit(); //马达初始化
BatteryCheckInit(); //电池电压监测初始化
IIC_Init(); //IIC初始化
#ifdef IMU_SW //使用软件解算
MPU6050_initialize();
#else
MPU6050_DMP_Initialize(); //初始化DMP引擎
#endif
//HMC5883L_SetUp(); //初始化磁力计HMC5883L
NRF24L01_INIT(); //NRF24L01初始化
PowerOn(); //开机等待
BT_ATcmdWrite(); //蓝牙写配置
BatteryCheck();
MS5611_Init();
IMU_Init(); // sample rate and cutoff freq. sample rate is too low now due to using dmp.
#ifdef UART_DEBUG
//定时器3初始化,串口调试信息输出
TIM3_Init(SysClock,2000);
#endif
//定时器4初始化,用于飞控主循环基准定时
TIM4_Init(SysClock,1000);
MotorPwmFlash(10,10,10,10);
altCtrlMode=MANUAL;
WaitBaroInitOffset(); //等待气压初始化高度完成
//飞控控制主循环
while (1)
{
}//end of while(1)
}
这里先贴出的是while(1)函数之前的初始化代码,按理来说光是研究飞控算法的话不用太去深究这些初始化配置函数,但为了也学习下stm32单片机我准备还是多花点功夫去研究下!我们就一个一个函数来说
1)首先是SystemClock_HSE(9)这个函数。HSE是高速外部时钟,可接石英/陶瓷谐振器或接外部时钟源,范围是4MHZ-16MHZ。但这了的9并不代表选择了9MHZ。我们再来看这个函数定义
可以看到这个有参函数定义的参数名是PLL,在这里指的就是锁相环频倍数,也就是就9倍,以8MHZ的外部时钟作为时钟输入源,在这里就是初始化系统时钟8*9=72MHZ;
2)cycleCounterInit()如果到这个追终到这个函数的定义里面去看很容易就发现这就是个得出当前系统时钟频率的函数
3)SysTick_Config(SystemCoreClock / 1000):这是个SysTick定时器的初始化函数,一开始我不明白为什么还要初始化这个SysTick定时器,不是只用到了TIM3和TIM4两个定时器来进行程序的定时中断处理吗。通过查询才明白这个定时器多是为了精准的延时而使用的。首先它是已计数的方式定时,72M的主频就是每隔1/72M秒计数加一,要实现1MS的中断就是0.001/1/72M=72000次。达到计数后会进入定时中断处理函数中
void SysTickHandler(void)
{
TimingDelay_Decrement();
}
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
这个TimingDelay又在哪里被赋值的呢?我们再看看Delay()函数
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
4)UART1_init(SysClock,BT_BAUD_Set);就是一个简单的串口初始化,设置串口和波特率
5)NVIC_INIT();我们直接看函数的定义
void NVIC_INIT(void)
{
TimerNVIC_Configuration();//定时器中断配置
UART1NVIC_Configuration();//串口1中断配置
}
void TimerNVIC_Configuration()
{
NVIC_InitTypeDef NVIC_InitStructure;
/* NVIC_PriorityGroup 2 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//TIM3
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//串口打印定时器,优先级低于姿态解算
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//TIM4
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//飞控主循环基准定时器,优先级高于串口打印
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void UART1NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USART1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
可以看到在里面设置了串口和定时器中断的先占优先级和从优先级并使能中断。
5)LoadParamsFromEEPROM();字面意思就很好理解,从外部储存器EEPROM中读取参数或给EEPROM中写入值;