目录
目录
一、预想功能
二、系统框架
三、硬件介绍
四、软件介绍
openmv程序
沁恒CH32V103最小系统
串口接收函数
pwm初始化
小车位置判断和控制
五、实际效果
学习引用文章:
一、预想功能
制作基于沁恒CH32V103最小系统和openmv的颜色追踪小车,他可以识别到所需要的颜色进而追踪所需颜色。
二、系统框架
三、硬件介绍
openmv:由openmv识别所需的色块,并且把所识别的色块中心xy坐标以数据包的形式发送给沁恒CH32V103最小系统。
沁恒CH32V103最小系统:接收openmv所发送过来的数据包,经过对xy坐标的判断和计算去通过pwm控制电机。
l298n:电机驱动模块,接收沁恒CH32V103最小系统的pwm来控制两边的两个电机。
四、软件介绍
openmv程序
import sensor, image, time, math
from pyb import UART
import json
import ustruct
blue_threshold = (25, 67, -37, 26, -63, -26)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()
uart = UART(3,115200) #定义串口3变量
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
def find_max(blobs): #定义寻找色块面积最大的函数
max_size=0
for blob in blobs:
if blob.pixels() > max_size:
max_blob=blob
max_size = blob.pixels()
return max_blob
def sending_data(cx,cy):
global uart;
#frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
#data = bytearray(frame)
data = ustruct.pack("
沁恒CH32V103最小系统
串口接收函数
#include "Uart.H"
#include "Motor.H"
//u8 x_coordinate;
u8 data_Correctness;
u16 openmv_data[7];
u8 number=0;
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
//========================================================================
// 函数: void USARTx_CFG(void)
// 描述: 串口初始化函数
// 参数: 无
// 返回: 无
// 版本:
// 日期:
// 备注:USART2 TX-->A.2 RX-->A.3
//========================================================================
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
USART_DeInit(USART2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置PA2为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置PA3为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; //设置串口波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
USART_Cmd(USART2, ENABLE); //使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启中断
}
//========================================================================
// 函数: void USART2_IRQHandler(void)
// 描述: 串口2中断函数
// 参数: 无
// 返回: 无
// 版本:
// 日期:
// 备注:接收openmv传来的数据
//========================================================================
void USART2_IRQHandler(void)
{
u8 temp;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
temp = USART_ReceiveData(USART2); //接收数据
Openmv_Receive_Data(temp);
}
}
void Openmv_Receive_Data(u16 data)//接收Openmv传过来的数据
{
static u8 state = 0;
if(state==0&&data==0x2C)
{
state=1;
openmv_data[number++]=data;
}
else if(state==1&&data==18)
{
state=2;
openmv_data[number++]=data;
}
else if(state==2)
{
openmv_data[number++]=data;
if(number>19||data == 0x5B) state=3; //the last of char is openmv[19]
}
else if(state==3) //state == 3 检测是否接受到结束标志
{
if(openmv_data[number-1] == 0x5B)
{
state = 0;
data_Correctness = 1;
USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);
}
else //wrong thing
{
state = 0;
number=0;
}
}
else //wrong thing
{
state = 0;
number=0;
}
}
void USART2_Rx_Task(void)
{
u16 posX,posY;
if(data_Correctness == 1)
{
posX = openmv_data[3]<<8 | openmv_data[2];
posY = openmv_data[5]<<8 | openmv_data[4];
data_Correctness = 0;
number = 0;
car_control(posX,posY);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
}
}
pwm初始化
#include "pwm.h"
//========================================================================
// 函数: TIM1_PWMOut_Init( u16 arr, u16 psc, u16 ccp )
// 描述: pwm初始化函数
// 参数: arr 周期,psc 预分频器值,ccp 占空比
// 返回: 无
// 版本:
// 日期:
// 备注:
//========================================================================
void TIM1_PWMOut_Init( u16 arr, u16 psc, u16 ccp )
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimebaseInitTypeDef TIM_TimebaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );//使能GPIOA外设时钟和TIM1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9; //配置PA8引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置输出速度:50MHz
GPIO_Init( GPIOA, &GPIO_InitStructure ); //GPIO初始化
TIM_TimebaseInitStructure.TIM_Period = arr; //指定下次更新事件时要加载到活动自动重新加载寄存器中的周期值。
TIM_TimebaseInitStructure.TIM_Prescaler = psc; //指定用于划分TIM时钟的预分频器值。
TIM_TimebaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频因子
TIM_TimebaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM计数模式,向上计数模式
TIM_TimebaseInit( TIM1, &TIM_TimebaseInitStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //指定TIM模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//指定TIM输出比较状态,即使能比较输出
TIM_OCInitStructure.TIM_Pulse = ccp; //指定要加载到捕获比较寄存器中的脉冲值。
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //指定输出极性。
TIM_OC1Init( TIM1, &TIM_OCInitStructure ); //根据TIM_OCInitStruct中指定的参数初始化TIM1 Channel1。
TIM_OC2Init( TIM1, &TIM_OCInitStructure );
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable ); //使能CCR1上的TIM1外设预加载寄存器
TIM_OC2PreloadConfig( TIM1, TIM_OCPreload_Disable );
TIM_CtrlPWMOutputs(TIM1, ENABLE ); //启用定时器1PWM输出
TIM_ARRPreloadConfig( TIM1, ENABLE ); //使能ARR上TIM1外设预加载寄存器
TIM_Cmd( TIM1, ENABLE ); //使能TIM1
}
小车位置判断和控制
#include "Motor.H"
u8 motor;
u8 left = 0;
u8 right = 1;
u8 run = 2;
void car_control(u16 x_coordinate,u16 y_coordinate)
{
if(x_coordinate < 50 && x_coordinate != 0)
{
motor = left;
}
else if (x_coordinate >= 200 && x_coordinate != 0)
{
motor = right;
}
else if (x_coordinate > 50 && x_coordinate < 200 && y_coordinate >0)
{
motor = run;
}
switch(motor)
{
case 0:TIM_SetCompare1(TIM1,400);
TIM_SetCompare2(TIM1,100);
break;
case 1:TIM_SetCompare1(TIM1,100);
TIM_SetCompare2(TIM1,400);
break;
case 2:TIM_SetCompare1(TIM1,x_coordinate/2-y_coordinate/4);
TIM_SetCompare2(TIM1,x_coordinate/2+y_coordinate/4);
break;
}
}
五、实际效果
openmv追踪小车
学习引用文章:
(4条消息) 使用openMV3与stm32进行通讯_qq_43243338的博客-CSDN博客_openmv与stm32通信
(4条消息) 【DIY】基于OpenMV的STM32追球小车_淹死的鱼-CSDN博客_openmv stm32
import sensor, image, time, math
from pyb import UART
import json
import ustruct
blue_threshold = (25, 67, -37, 26, -63, -26)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()
uart = UART(3,115200) #定义串口3变量
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
def find_max(blobs): #定义寻找色块面积最大的函数
max_size=0
for blob in blobs:
if blob.pixels() > max_size:
max_blob=blob
max_size = blob.pixels()
return max_blob
def sending_data(cx,cy):
global uart;
#frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
#data = bytearray(frame)
data = ustruct.pack("
沁恒CH32V103最小系统
串口接收函数
#include "Uart.H"
#include "Motor.H"
//u8 x_coordinate;
u8 data_Correctness;
u16 openmv_data[7];
u8 number=0;
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
//========================================================================
// 函数: void USARTx_CFG(void)
// 描述: 串口初始化函数
// 参数: 无
// 返回: 无
// 版本:
// 日期:
// 备注:USART2 TX-->A.2 RX-->A.3
//========================================================================
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
USART_DeInit(USART2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置PA2为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置PA3为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; //设置串口波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
USART_Cmd(USART2, ENABLE); //使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启中断
}
//========================================================================
// 函数: void USART2_IRQHandler(void)
// 描述: 串口2中断函数
// 参数: 无
// 返回: 无
// 版本:
// 日期:
// 备注:接收openmv传来的数据
//========================================================================
void USART2_IRQHandler(void)
{
u8 temp;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
temp = USART_ReceiveData(USART2); //接收数据
Openmv_Receive_Data(temp);
}
}
void Openmv_Receive_Data(u16 data)//接收Openmv传过来的数据
{
static u8 state = 0;
if(state==0&&data==0x2C)
{
state=1;
openmv_data[number++]=data;
}
else if(state==1&&data==18)
{
state=2;
openmv_data[number++]=data;
}
else if(state==2)
{
openmv_data[number++]=data;
if(number>19||data == 0x5B) state=3; //the last of char is openmv[19]
}
else if(state==3) //state == 3 检测是否接受到结束标志
{
if(openmv_data[number-1] == 0x5B)
{
state = 0;
data_Correctness = 1;
USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);
}
else //wrong thing
{
state = 0;
number=0;
}
}
else //wrong thing
{
state = 0;
number=0;
}
}
void USART2_Rx_Task(void)
{
u16 posX,posY;
if(data_Correctness == 1)
{
posX = openmv_data[3]<<8 | openmv_data[2];
posY = openmv_data[5]<<8 | openmv_data[4];
data_Correctness = 0;
number = 0;
car_control(posX,posY);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
}
}
pwm初始化
#include "pwm.h"
//========================================================================
// 函数: TIM1_PWMOut_Init( u16 arr, u16 psc, u16 ccp )
// 描述: pwm初始化函数
// 参数: arr 周期,psc 预分频器值,ccp 占空比
// 返回: 无
// 版本:
// 日期:
// 备注:
//========================================================================
void TIM1_PWMOut_Init( u16 arr, u16 psc, u16 ccp )
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimebaseInitTypeDef TIM_TimebaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );//使能GPIOA外设时钟和TIM1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9; //配置PA8引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置输出速度:50MHz
GPIO_Init( GPIOA, &GPIO_InitStructure ); //GPIO初始化
TIM_TimebaseInitStructure.TIM_Period = arr; //指定下次更新事件时要加载到活动自动重新加载寄存器中的周期值。
TIM_TimebaseInitStructure.TIM_Prescaler = psc; //指定用于划分TIM时钟的预分频器值。
TIM_TimebaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频因子
TIM_TimebaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM计数模式,向上计数模式
TIM_TimebaseInit( TIM1, &TIM_TimebaseInitStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //指定TIM模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//指定TIM输出比较状态,即使能比较输出
TIM_OCInitStructure.TIM_Pulse = ccp; //指定要加载到捕获比较寄存器中的脉冲值。
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //指定输出极性。
TIM_OC1Init( TIM1, &TIM_OCInitStructure ); //根据TIM_OCInitStruct中指定的参数初始化TIM1 Channel1。
TIM_OC2Init( TIM1, &TIM_OCInitStructure );
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable ); //使能CCR1上的TIM1外设预加载寄存器
TIM_OC2PreloadConfig( TIM1, TIM_OCPreload_Disable );
TIM_CtrlPWMOutputs(TIM1, ENABLE ); //启用定时器1PWM输出
TIM_ARRPreloadConfig( TIM1, ENABLE ); //使能ARR上TIM1外设预加载寄存器
TIM_Cmd( TIM1, ENABLE ); //使能TIM1
}
小车位置判断和控制
#include "Motor.H"
u8 motor;
u8 left = 0;
u8 right = 1;
u8 run = 2;
void car_control(u16 x_coordinate,u16 y_coordinate)
{
if(x_coordinate < 50 && x_coordinate != 0)
{
motor = left;
}
else if (x_coordinate >= 200 && x_coordinate != 0)
{
motor = right;
}
else if (x_coordinate > 50 && x_coordinate < 200 && y_coordinate >0)
{
motor = run;
}
switch(motor)
{
case 0:TIM_SetCompare1(TIM1,400);
TIM_SetCompare2(TIM1,100);
break;
case 1:TIM_SetCompare1(TIM1,100);
TIM_SetCompare2(TIM1,400);
break;
case 2:TIM_SetCompare1(TIM1,x_coordinate/2-y_coordinate/4);
TIM_SetCompare2(TIM1,x_coordinate/2+y_coordinate/4);
break;
}
}
五、实际效果
openmv追踪小车
学习引用文章:
(4条消息) 使用openMV3与stm32进行通讯_qq_43243338的博客-CSDN博客_openmv与stm32通信
(4条消息) 【DIY】基于OpenMV的STM32追球小车_淹死的鱼-CSDN博客_openmv stm32



