(三)stm32巡线小车首尝试 测速篇

#采用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();
    }
}

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值