#采用stm32f103c8t6芯片
这回是测速篇了!越来越难了,呜呜呜。
这里的测速,指的是测量轮子的转速,来确定车子前进的方向,同时方便控制小车的转向。我手上的测速元件是u型的光电传感器,型号是moc70t3。我可以通过传感器被遮挡的时间与次数,来计算小车车轮的速度。
遮挡的元件大概长这样:
与红外线模块一样,这个传感器是三个输出的引脚,控制也差不多,所以我直接把红外线的工程复制,做了模板。不过,需要注意的是,这个模块的输出逻辑和红外线是反的[烦恼]!接收到信号时是高电平,信号被遮挡时是低电平。
有了测速元件,可是怎么测速呢?我的想法是,轮子开始转动,检测到第一个空隙时(第一个下降沿)开始计时,检测到下一个上升沿时停止计时,计时器清零。两者时间差便是通过一片遮挡片时的时间,这样子就可以衡量轮子旋转的速度了。
好的,不多说,开始撸代码!
我在红外线的模板上做了一些稍微的改动。光电传感器的初始化我用了两个而不是一个.c文件,这主要的原因是参数初始化加入了结构体,如果不使用两个c文件进行容纳,会导致主函数的代码量变多,也不利于后期对于参数的调整需求。
测速结构:
SPEED_Set主要是一些光电传感器的基础支持函数,包括使能时钟等,包含在speed.c中,在speed.h中声明。
#speed.c 未完成
#include "speed.h"
#include "stm32f10x.h"
void SPEED_Set(MySPEED_InitTypedef* input_speed_set)
{
GPIO_InitTypeDef GPIO_init_set;
if (input_speed_set->input_GPIOx == GPIOA)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
else if (input_speed_set->input_GPIOx == GPIOB)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
else if (input_speed_set->input_GPIOx == GPIOC)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
else if (input_speed_set->input_GPIOx == GPIOD)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_init_set.GPIO_Mode = GPIO_Mode_IPU;
GPIO_init_set.GPIO_Pin = input_speed_set->Pin;
GPIO_init_set.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(input_speed_set->input_GPIOx, &GPIO_init_set);
}
而SPEED_Init则是无参数函数,负责真正的初始化。在SPEED_Init中设置好四个光电传感器结构体的参数,然后分别传入SPEED_Set中进行初始化。SPEED_Init函数在SPEED_Init.c中,并由SPEED_init.h声明。其中我还打算将速度获取放置至speed_init.c中。
继续写了!
在SPEED_Set函数中我仅仅配置了中断,接下来还需配置中断设置,紧接着还需要配置中断服务函数,才可以完成整体的框架。加油!
中断的配置比较简单,需要GPIO_EXTILineConfig来设置io与中断线映射,然后通过EXTI_Init配置相应的中断线。其中io映射需要输入对应gpio与pin,配置中断线需要确定配置哪一根中断线,因此,我将这三个参数写进了结构体中。
最终的结构体便是这样的:
typedef struct
{
GPIO_TypeDef* input_GPIOx; // 光电传感器传入数据GPIO
uint16_t Pin; // 光电传感器具体引脚
uint32_t EXTI_Line_set; // 中断线设置,与Pin对应
uint8_t GPIO_port_source; // 设置中断线对应GPIOx, EXTI_Init
uint8_t GPIO_pin_source; // 设置中断线GPIO_pin, EXTI_Tnit
} MySPEED_InitTypedef;
之后,便是配置nvic了,我出于简单考虑,将中断地抢占与响应优先级均设置为1,并且针对不同不同地EXTI_Line写了不同地nvic配置,用以适应不同地结构体输入。
SPEED_Set中的配置基础函数都已经完成了,接下来便是在speed_init.c中完成具体设置了。
首先,定义结构体变量并填入参数,这个比较简单,就不赘述了。然后开始写中断服务函数(不要忘掉清除中断标志位啊!!!)。
中断函数的具体思路是,到达第一个下降沿获取并记录时间,到达第一个上升沿再获取一个新的时间,两个时间相减便可以获得经过一个遮挡的时间间隔,进而获得旋转速度。其中时间由定时器中断提供,定时器每溢出一次,定义的整型变量就会加1,通过获取变量的值就可以做出一个准确的计时器。这样,通过下降沿和上升沿的时间差,取倒数,便可以获得轮子的速度(注意这里只是衡量表准,单位并不是标准的m/s之类的)。
框架建立完成了,接下来的优化问题,我会在小车整体实验的时候仔细考虑。
好了,就先写到这,附上代码!
计时器代码:
#timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"
#define TIMER_base 719, 99 // 分别是arr和psc,即计数器初值和预分频系数,1ms溢出一次
#define TIMER_reset_ENABLE 0 // 定时器重置为0
#define TIMER_reset_DISABLE 1 // 定时器不重置
// 向上计数
void TIMER_Init(TIM_TypeDef* input_TIMx);
int TIME_calculate(void); // 若arr或psc更改,该函数必须更改
void TIMER_Stop(TIM_TypeDef* input_TIMx, int flag); // 时钟停止,flag决定是否重置
void TIMER_Start(TIM_TypeDef* input_TIMx);
#endif
#timer.c
#include "timer.h"
#include "stm32f10x.h"
volatile unsigned int timer2_counter = 0; // 防止编译器优化导致进入中断造成错误
void TIME_BaseInit(TIM_TypeDef* base_input_TIMx, u16 arr, u16 psc)
{
// arr计时器初值,psc分频系数
TIM_TimeBaseInitTypeDef TIM_base_init_set;
TIM_base_init_set.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_base_init_set.TIM_CounterMode = TIM_CounterMode_Up;
TIM_base_init_set.TIM_Period = arr;
TIM_base_init_set.TIM_Prescaler = psc;
TIM_TimeBaseInit(base_input_TIMx, &TIM_base_init_set);
}
void TIMER_Init(TIM_TypeDef* input_TIMx)
{
NVIC_InitTypeDef NVIC_init_set;
if (input_TIMx == TIM2)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
else if (input_TIMx == TIM3)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
else if (input_TIMx == TIM4)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIME_BaseInit(input_TIMx, TIMER_base); // 基准设置
TIM_ITConfig(input_TIMx, TIM_IT_Update, ENABLE); // 使能计时器中断
NVIC_init_set.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_init_set.NVIC_IRQChannelSubPriority = 1;
NVIC_init_set.NVIC_IRQChannelCmd = ENABLE;
if (input_TIMx == TIM2)
NVIC_init_set.NVIC_IRQChannel = TIM2_IRQn;
else if (input_TIMx == TIM3)
NVIC_init_set.NVIC_IRQChannel = TIM3_IRQn;
else if (input_TIMx == TIM4)
NVIC_init_set.NVIC_IRQChannel = TIM4_IRQn;
NVIC_Init(&NVIC_init_set);
TIM_Cmd(input_TIMx, ENABLE);
}
int TIME_calculate(void)
{
// 如果大于1ms的溢出时间可能会出错
return timer2_counter;
}
void TIMER_Stop(TIM_TypeDef* input_TIMx, int flag)
{
TIM_Cmd(input_TIMx, DISABLE);
if (flag == TIMER_reset_ENABLE)TIM_SetCounter(input_TIMx, 0);
}
void TIMER_Start(TIM_TypeDef* input_TIMx)
{
TIM_Cmd(input_TIMx, ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
// 注意可能会溢出,虽然溢出时间很长,但终归有可能溢出
timer2_counter++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
光电传感器:
#speed.h
// 速度传感器,接收到信号时高电平,被遮挡时低电平
#ifndef __SPEED_H
#define __SPEED_H
#include "stm32f10x.h"
typedef struct
{
GPIO_TypeDef* input_GPIOx; // 光电传感器传入数据GPIO
uint16_t Pin; // 光电传感器具体引脚
uint32_t EXTI_Line_set; // 中断线设置,与Pin对应
uint8_t GPIO_port_source; // 设置中断线对应GPIOx, EXTI_Init
uint8_t GPIO_pin_source; // 设置中断线GPIO_pin, EXTI_Tnit
} MySPEED_InitTypeDef;
void SPEED_Set(MySPEED_InitTypeDef* input_speed_set);
#endif
#speed.c
// 光电传感器基础配置服务
#include "speed.h"
#include "stm32f10x.h"
void SPEED_Set(MySPEED_InitTypeDef* input_speed_set)
{
GPIO_InitTypeDef GPIO_init_set;
EXTI_InitTypeDef EXTI_init_set;
NVIC_InitTypeDef NVIC_init_set;
if (input_speed_set->input_GPIOx == GPIOA)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
else if (input_speed_set->input_GPIOx == GPIOB)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
else if (input_speed_set->input_GPIOx == GPIOC)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
else if (input_speed_set->input_GPIOx == GPIOD)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
// 光电传感器所需gpio配置
GPIO_init_set.GPIO_Mode = GPIO_Mode_IPU;
GPIO_init_set.GPIO_Pin = input_speed_set->Pin;
GPIO_init_set.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(input_speed_set->input_GPIOx, &GPIO_init_set);
GPIO_EXTILineConfig(input_speed_set->GPIO_port_source, input_speed_set->GPIO_pin_source); // io与中断线映射
// 初始化,设置触发方式等
EXTI_init_set.EXTI_Line = input_speed_set->EXTI_Line_set; // 中断线设置
EXTI_init_set.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_init_set.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_init_set.EXTI_LineCmd = ENABLE;
// 中断使能
NVIC_init_set.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_init_set.NVIC_IRQChannelSubPriority = 1;
NVIC_init_set.NVIC_IRQChannelCmd = ENABLE;
if (input_speed_set->EXTI_Line_set == EXTI_Line0)
NVIC_init_set.NVIC_IRQChannel = EXTI0_IRQn;
else if (input_speed_set->EXTI_Line_set == EXTI_Line1)
NVIC_init_set.NVIC_IRQChannel = EXTI1_IRQn;
else if (input_speed_set->EXTI_Line_set == EXTI_Line2)
NVIC_init_set.NVIC_IRQChannel = EXTI2_IRQn;
else if (input_speed_set->EXTI_Line_set == EXTI_Line3)
NVIC_init_set.NVIC_IRQChannel = EXTI3_IRQn;
else if (input_speed_set->EXTI_Line_set == EXTI_Line4)
NVIC_init_set.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_Init(&NVIC_init_set);
EXTI_Init(&EXTI_init_set);
}
#speed_init.h
#ifndef __SPEED_INIT_H
#define __SPEED_INIT_H
#include "speed.h"
void SPEED_Init(void); // 初始化四个光电传感器(虽然目前只写了一个)
float speed_calculate1(void); // 第一个光电传感器测量
void SPEED_Stop(void); // 停止四个速度测量
void SPEED_Start(void); // 开始四个速度测量
// 配置光电传感器定时时钟
#define SPEED_TIMER TIM2
// SPEED1 设置
#define SPEED1_GPIO GPIOA
#define SPEED1_Pin GPIO_Pin_1
#define SPEED1_GPIO_port_source GPIO_PortSourceGPIOA
#define SPEED1_GPIO_pin_source GPIO_PinSource1
#define SPEED1_EXTI_Line EXTI_Line1
#define SPEED1_input GPIO_ReadInputDataBit(SPEED1_GPIO, SPEED1_Pin)
#endif
#speed_init.c
#include "speed_init.h"
#include "stm32f10x.h"
#include "timer.h"
float car_speed_calculate1;
void SPEED_Init(void)
{
MySPEED_InitTypeDef speed_init_set1, speed_init_set2,
speed_init_set3, speed_init_set4;
// 第一个设置
speed_init_set1.input_GPIOx = SPEED1_GPIO;
speed_init_set1.Pin = SPEED1_Pin;
speed_init_set1.GPIO_port_source = SPEED1_GPIO_port_source;
speed_init_set1.GPIO_pin_source = SPEED1_GPIO_pin_source;
speed_init_set1.EXTI_Line_set = SPEED1_EXTI_Line;
SPEED_Set(&speed_init_set1);
}
void SPEED_Stop(void)
{
TIMER_Stop(SPEED_TIMER, TIMER_reset_ENABLE);
}
void SPEED_Start(void)
{
TIMER_Start(SPEED_TIMER);
}
void EXTI1_IRQHandler(void)
{
static int odd_even = 0;
static int time1 = 0;
static int time_difference = 0;
if (odd_even == 0 && SPEED1_input == 0)
{
time1 = TIME_calculate();
odd_even = 1;
}
else if (odd_even == 1 && SPEED1_input == 1)
{
time_difference = TIME_calculate() - time1;
// 判断合理性
if (time_difference >= 0)
{
// 速度计算,乘系数
car_speed_calculate1 = 10000.0/(TIME_calculate() - time1);
}
odd_even = 0;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
float speed_calculate1(void)
{
return car_speed_calculate1;
}
主程序:
#include "stm32f10x.h"
#include "led.h"
#include "speed_init.h"
#include "timer.h"
void initSet(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
LED1_PC13_Off;
SPEED_Init();
TIMER_Init(SPEED_TIMER);
}
int main(void)
{
float speed;
initSet();
while(1)
{
speed = speed_calculate1();
}
}