使用器材
控制类
STM32F103c8t6最小系统板
驱动类
L289n电机驱动模块 刷电机
图像捕获类
OV760摄像头
程序思路
传递周围道路信息
传递当前小车的速度和方向等信息
计算相关值并将数据返还给主控函数
传递信息的主要通道
向主控函数提供电压值,方便控制小车速度
控制电机的转速
控制小车运动方向
主程序
启动
初始化
定时器
红外管
pid算法模块
串口初始化
电机驱动
舵机驱动
控制函数
摄像头
调试过程
电机驱动模块
void moter_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(MOTER_IN1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = MOTER_IN1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOTER_IN1_PORT, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(MOTER_IN2_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = MOTER_IN2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOTER_IN2_PORT, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(MOTER_IN3_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = MOTER_IN3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOTER_IN3_PORT, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(MOTER_IN4_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = MOTER_IN4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOTER_IN4_PORT, &GPIO_InitStructure);
}
在本段代码的调试过程中,我队遇到了些问题,小车始终有一个轮子不能正常转动,后来发现是下面的代码在编写过程中写错了引脚,而且写错了GPIO的工作模式
GPIO_InitStructure.GPIO_Pin = MOTER_IN4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
摄像头模块
在对摄像头模块的调试过程中,我队发现摄像头根本没有向控制函数传输数据,因为在使用野火串口调试助手时无论如何都无法看到数据 摄像头部分代码如下:
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "ov7670.h"
#include "sccb.h"
#include "bsp_SysTick.h
u16 Row[320];
u16 Color[240][320];
int main(void)
{
u16 i;
u16 t=0;
u16 pixcnt=0;
u16 linecnt=0;
systick_init(72);
初始化USART 配置模式为 115200 8-N-1,中断接收
USART_Config();
Usart_SendString( DEBUG_USARTx,"这是一个串口中断接收回显实验\n");
printf("欢迎使用野火STM32开发板\n\n\n\n");
if(OV7670_Init())
printf("初始化失败");
delay_ms(1500);
OV7670_Window_Set(10,174,240,320);
delay_ms(1500);
while(OV7670_VSYNC==0);
while(OV7670_VSYNC==1) 只有在VSYNC为低时,才传输数据
for(linecnt=0;linecnt<240;linecnt++)
{
while(OV7670_HREF==0);
for(pixcnt=0;pixcnt<320;pixcnt++)
{
while(OV7670_PCLK==0);
Row[pixcnt]=((GPIOB->IDR&0XF000))>>8;
while(OV7670_PCLK==1);
while(OV7670_PCLK==0);
Row[pixcnt]|=(GPIOB->IDR&0XF000)>>8;
while(OV7670_PCLK==1);
Row[pixcnt]=((Row[pixcnt]&0xF800)>>11) + ((Row[pixcnt]&0x07E0)>>5) + (Row[pixcnt]&0x001F);
if(Row[pixcnt]>0x7E) Row[pixcnt]= 0xffff ;
else
{ Row[pixcnt]=0x00 ;
t++;
}
t++;
}
for(i=0;i<320;i++)
printf("%d ",Row[i]);
}
printf("黑色像素点的个数:%d",t);
while(1)
{
while(OV7670_VSYNC==0);
while(OV7670_VSYNC==1)
for(linecnt=0;linecnt<240;linecnt++)
{
while(OV7670_HREF==0);
for(pixcnt=0;pixcnt<320;pixcnt++)
{
while(OV7670_PCLK==0);
Row[pixcnt]=(((GPIOC->IDR&0X0F00)+(GPIOB->IDR&0XF000))>>8);
while(OV7670_PCLK==1);
while(OV7670_PCLK==0);
while(OV7670_PCLK==1);
Row[pixcnt]=((Row[pixcnt]&0xF800)>>11) + ((Row[pixcnt]&0x07E0)>>5) + (Row[pixcnt]&0x001F);
if(Row[pixcnt]>0x7E) Row[pixcnt]= 0xffff ;
else
{ Row[pixcnt]=0x00 ;
}
}
for(i=0;i<320;i++)
printf("%d ",Row[i]);
}
}
}
经过我队不断调试和在网上收集相关资料,最后将问题锁定在摄像头的时钟频率上,我队用最小系统板的PA8引脚向摄像头提供频率,但是由于系统板二分频的原因,导致分给摄像头的频率不够,最后我队决定将系统板超频到96M,经过二分频后时48M,此时摄像头正常工作。
舵机模块
部分代码如下:
static void GENERAL_TIM_Mode_Config(void)
{
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period-1;
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler-1;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(& TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM4, ENABLE);
TIM_CtrlPWMOutputs(TIM4,ENABLE);
TIM_Cmd(GENERAL_TIM, ENABLE);
}
void GENERAL_TIM_Init(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
}
控制函数中舵机模块的代码
turn_left();
Delay_ms(2000);
left_back();
Delay_ms(2000);
turn_right();
Delay_ms(2000);
right_back();
Delay_ms(2000);
receiving_process();
在本模块中,我队原来是直接调用turn_left和turn_right函数进行转向,但是发现转动角度及其微小。 后来,通过添加延时函数,让舵机运行的时间延长一会,成功获得了较大角度的转向。
PID调速图像
部分模块关键代码
定时器模块
static void GENERAL_TIM_Mode_Config(void)
{
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period-1;
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler-1;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(& TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM4, ENABLE);
TIM_CtrlPWMOutputs(TIM4,ENABLE);
TIM_Cmd(GENERAL_TIM, ENABLE);
}
void GENERAL_TIM_Init(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
}
高级定时器模块
static void ADVANCE_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQ ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
static void ADVANCE_TIM_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK, ENABLE);
TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_Period;
TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_Prescaler;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
TIM_ClearFlag(ADVANCE_TIM, TIM_FLAG_Update);
TIM_ITConfig(ADVANCE_TIM,TIM_IT_Update,ENABLE);
TIM_Cmd(ADVANCE_TIM, ENABLE);
}
void ADVANCE_TIM_Init(void)
{
ADVANCE_TIM_NVIC_Config();
ADVANCE_TIM_Mode_Config();
}
红外管模块
#include "honwai.h"
u8 L2_Val, L1_Val, M_Val, R1_Val, R2_Val;
int Line_Num;
void honwai_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( LEFTHONWAI_GPIO_CLK|RIGHTHONWAI_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = LEFTONE_GPIO_PIN|LEFTMOST_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(LEFTHONWAI_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin =RIGHTMOST_GPIO_PIN|RIGHTONE_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(RIGHTHONWAI_GPIO_PORT, &GPIO_InitStructure);
}
void Light_GoStraight_control(void)
{
get_line_ttl();
if(L1_Val == 0 && M_Val == 1 && R1_Val == 0 ) Line_Num = 0;
if(L1_Val == 0 && M_Val == 1 && R1_Val == 1) Line_Num = 550;
if(L1_Val == 0 && M_Val == 0 && R1_Val == 1 ) Line_Num =600;
if(L1_Val == 1 && M_Val == 0 && R1_Val == 0) Line_Num = -600;
if(L1_Val == 1 && M_Val == 1 && R1_Val == 0) Line_Num = -550;
}
void get_line_ttl(void)
{
if(left_most==SET){L2_Val = 1;} else {L2_Val = 0;}
if(left_one == SET){L1_Val = 1;} else {L1_Val = 0;}
if(right_one == SET){R1_Val = 1;} else {R1_Val = 0;}
if(right_most == SET){R2_Val = 1;} else {R2_Val = 0;}
}
PID算法模块
#include "./pid/bsp_pid.h"
_pid leftpid_location;
_pid leftpid_speed;
_pid rightpid_location;
_pid rightpid_speed;
void leftPID_param_init(void)
{
leftpid_location.target_val=0.0;
leftpid_location.actual_val=0.0;
leftpid_location.err=0.0;
leftpid_location.err_last=0.0;
leftpid_location.integral=0.0;
leftpid_location.Kp = 0.05;
leftpid_location.Ki = 0.001 ;
leftpid_location.Kd = 0;
leftpid_speed.target_val=0.0;
leftpid_speed.actual_val=0.0;
leftpid_speed.err=0.0;
leftpid_speed.err_last=0.0;
leftpid_speed.integral=0.0;
leftpid_speed.Kp = 5.0;
leftpid_speed.Ki = 0.7;
leftpid_speed.Kd = 1.5;
}
void rightPID_param_init(void)
{
rightpid_location.target_val=0.0;
rightpid_location.actual_val=0.0;
rightpid_location.err=0.0;
rightpid_location.err_last=0.0;
rightpid_location.integral=0.0;
rightpid_location.Kp = 0.05;
rightpid_location.Ki = 0.001;
rightpid_location.Kd = 0;
rightpid_speed.target_val=0.0;
rightpid_speed.actual_val=0.0;
rightpid_speed.err=0.0;
rightpid_speed.err_last=0.0;
rightpid_speed.integral=0.0;
rightpid_speed.Kp = 5.0;
rightpid_speed.Ki = 0.7;
rightpid_speed.Kd = 1.5;
}
void set_pid_target(_pid *pid, float temp_val)
{
pid->target_val = temp_val;
}
float get_pid_target(_pid *pid)
{
return pid->target_val;
}
void set_p_i_d(_pid *pid, float p, float i, float d)
{
pid->Kp = p;
pid->Ki = i;
pid->Kd = d;
}
float location_pid_realize(_pid *pid, float actual_val)
{
pid->err = pid->target_val - actual_val;
if((pid->err >= -40) && (pid->err <= 40))
{
pid->err = 0;
pid->integral = 0;
}
if (pid->err > -1500 && pid->err < 1500)
{
pid->integral += pid->err;
if (pid->integral > 4000)
pid->integral = 4000;
else if (pid->integral < -4000)
pid->integral = -4000;
}
pid->actual_val = pid->Kp * pid->err +
pid->Ki * pid->integral +
pid->Kd * (pid->err - pid->err_last);
pid->err_last = pid->err;
return pid->actual_val;
}
float speed_pid_realize(_pid *pid, float actual_val)
{
pid->err = pid->target_val - actual_val;
if((pid->err<0.2f ) && (pid->err>-0.2f))
pid->err = 0.0f;
pid->integral += pid->err;
pid->actual_val = pid->Kp * pid->err +
pid->Ki * pid->integral +
pid->Kd * (pid->err - pid->err_last);
pid->err_last = pid->err;
return pid->actual_val;
}
float curr_pid_realize(_pid *pid, float actual_val)
{
pid->err=pid->target_val-actual_val;
pid->integral += pid->err;
if (pid->integral > 2000)
pid->integral = 2000;
else if (pid->integral < -2000)
pid->integral = -2000;
pid->actual_val = pid->Kp * pid->err +
pid->Ki * pid->integral +
pid->Kd * (pid->err - pid->err_last);
pid->err_last=pid->err;
return pid->actual_val;
}
校赛总结
在本次校赛过程中,由于是线下比赛,队内沟通是个相当大的挑战,我队决定全天开启腾讯会议,线上交流,并且分模块,分功能,分阶段将整个工程细化分派给三名成员,由三名成员将各自的代码完成过后,交给队长统一协调,并完成最后的主控部分。在紧张的赛程中,遇到了很多艰难的挑战,队员难免出现退缩,畏惧的情绪,队员之间的不协调,出现矛盾也是正常的情况,好在不断调整,最后众志成城,其利断金,最后圆满完成了校赛的赛程,在这个过程中,我队成员不仅学习到了许多新的知识,更深厚了同学情谊,为日后应对其他挑战打下了坚实的基础。
|