(2023年全国大学生电子设计竞赛 E题)
1. 全国大学生电子设计竞赛概述
1.1全国大学生电子设计竞赛简介
全国大学生电子设计竞赛(National Undergraduate Electronics Design Contest,简称TI杯)是教育部和工信部共同发起的大学生学科竞赛之一,该竞赛面向大学生群众性科技活动,目的在于推动高等学校促进信息与电子类学科课程体系和课程内容改革。竞赛的特点是与高等学校相关专业的课程体系和课程内容改革密切结合,以推动课程教学、教学改革和实验室建设工作。
一般赛题主要有以下几大类型:
电源类:简易数控直流电源、直流稳压电源;
信号源类:实用信号源的设计和制作、波形发生器、电压控制LC振荡器等;
高频无线电类:简易无线电遥控系统、调幅广播收音机、短波调频接收机、调频收音机等;
放大器类:实用低频功率放大器、高效率音频功率放大器、宽带放大器等;
仪器仪表类:简易电阻、电容和电感测试仪、简易数字频率计、频率特性测试仪、数字式工频有效值多用表、简易数字存储示波器、低频数字式相位测量仪、简易逻辑分析仪;
数据采集与处理类:多路数据采集系统、数字化语音存储与回放系统、数据采集与传输系统;
控制类:水温控制系统、自动往返电动小汽车、简易智能电动车、液体点滴速度监控装置。
1.2 2023年全国大学生电子设计竞赛E题题目及要求
1.2.1任务
设计制作一个运动目标控制与自动追踪系统。系统包括模拟目标运动的红色光斑位置控制系统和指示自动追踪的绿色光斑位置控制系统。系统结构示意及摆放位置见图1(a) 。图中两个激光笔固定在各自独立的二维电控云台上。
红色激光笔发射的光斑用来模拟运动目标,光斑落在正前方距离 1m 处的白色屏幕上,光斑直径≤1cm。红色光斑位置控制系统控制光斑能在屏幕范围内任意移动。
绿色激光笔发射的光斑由绿色光斑位置系统控制,用于自动追踪屏幕上的红色光斑,指示目标的自动追踪效果,光斑直径≤1cm。绿色激光笔放置线段如图1(b)所示,该线段与屏幕平行,位于红色激光笔两侧,距红色激光笔距离大于0.4m、小于1m。绿色激光笔在两个放置线段上任意放置。
屏幕为白色,有效面积大于 0.6×0.6m(2)。用铅笔在屏幕中心画出一个边长0.5m 的正方形,标识屏幕的边线;所画的正方形的中心为原点,用铅笔画出原点位置,所用铅笔痕迹宽≤1mm。
1.2.2 基本要求
设置运动目标位置复位功能。执行此功能,红色光斑能从屏幕任意位置回到原点。光斑中心距原点误差≤2cm。
启动运动目标控制系统。红色光斑能在30秒内沿屏幕四周边线顺时针移动一周,移动时光斑中心距边线距离≤2cm。
用约1.8cm宽的黑色电工胶带沿 A4 纸四边贴一个长方形,构成 A4 靶纸。将此A4靶纸贴在屏幕自定的位置。启动运动目标控制系统,红色光斑能30秒内沿胶带顺时针移动一周。超时不得分,光斑完全脱离胶带一次扣2分,连续脱离胶带移动 5cm 以上记为0分。
将上述 A4 靶纸以任意旋转角度贴在屏幕任意位置。启动运动目标控制系统,要求同3。
1.2.3 发挥部分
运动目标位置复位,一键启动自动追踪系统,控制绿色光斑能在 2 秒内追踪红色光斑,追踪成功发出连续声光提示。此时两个光斑中心距离应≤3cm。
运动目标重复基本要求iii~iv的动作。绿色激光笔发射端可以放置在其放置线段的任意位置,同时启动运动目标及自动追踪系统,绿色光斑能自动追踪红色光斑。启动系统 2 秒后,应追踪成功,发出连续声光提示。此后,追踪过程中两个光斑中心距离大于 3cm 时,定义为追踪失败,一次扣 2 分。连续追踪失败3 秒以上记为 0 分。
运动目标控制系统和自动追踪系统均需设置暂停键。同时按下暂停键,红色和绿色光斑应立即制动,以便测量两个光斑中心距离。
其他。
1.2.4 说明
红色、绿色光斑位置控制系统必须相互独立,之间不得有任何方式通信;光斑直径小于 1cm;屏幕上无任何电子元件;控制系统不能采用台式计算机或笔记本电脑。不符合要求不进行测试。
基本要求iii、iv未得分不进行发挥部分ii 的测试。
详细题目内容参见网址:运动目标控制与自动追踪系统-全国大学生电子设计竞赛培训网
2. 系统总体设计方案
2.1任务概述
根据题目要求,参赛者需要设计一个运动目标控制和一个自动追踪系统,在基本要求中,需要使用运动目标控制系统来完成题目的要求;在发挥部分中,需要同时使用到运动目标控制系统和自动追踪系统,并且在自动追踪系统中需要有声光报警的功能。两个系统必须相互独立,之间不得有任何方式通信。两个系统的拜访示意图如下所示。
2.2 总体设计方案与论证
2.2.1 系统总体设计
使用步进电机云台制作了运动目标控制系统和自动追踪系统。在运动控制系统中控制红色激光笔发射的光斑模拟运动目标,完成对红色光斑的复位、固定轨迹移动及A4黑边循迹功能。由于步进电机有很高的控制精度,所以我们使用了自制的步进电机二维云台;使用DRV8825作为步进电机的驱动;使用OPENMV视觉模块识别矩形边框和红点的识别。使用CW32单片机作为整个系统的主控制器,协调各个模块。
由于本次电赛我们小组只拿到了省一,并且在比赛期间我们只做出来了运动目标控制系统,所以在下面的介绍中不包含自动追踪系统。
2.2.2 主控芯片的选型
方案一:stm32
STM32单片机主要是由意法半导体公司设计的微控制器,其具有低功耗、低成本和高性能的特点,适用于嵌入式应用。其采用ARM Cortex-O内核,根据其内核架构的不同,可以将其分成一系列产品,当前主流的产品包括STM32F0、STM32F1、STM32F3,具有超低功耗的产品包括STM32L0、STM32L1、STM32L4等。
方案二:CW32
CW32F030X6/X8 是由武汉芯源半导体推出的一款国产32位微控制器,基于 eFlash ,集成了主频高达 64MHz 的 ARM® Cortex®-M0+ 内核、高速嵌入式存储器(多至 64K 字节 FLASH 和多至 8K 字节 SRAM)以及一系列全面的增强型外设和 I/O 口。其优势如下: 1、宽工作电压:1.65V-5.5V,宽工作温度:-40℃-105℃。 2、采用Prefetch+Cache架构,同频CoreMark 计算力/功耗比超越同类产品。 3、12位高速ADC,可达到±1.0 LSB INL,11.3 ENOB(有效位数)。 4、通用高性能功能,提供3级程序安全防护。 5、稳定可靠的eFLASH制造,确保工业高可靠应用。 6、HBM ESD 8KV,全部ESD可靠性达到国际标准最高等级等。
综上所述:CW32F030C8T6由于其优异的表现完全能满足电子设计大赛的要求。
2.2.3 总体方案确定
运动控制系统由openMV将识别到的红色激光笔坐标发送给MCU进行处理。MCU经过一定的运算处理后控制云台带动红色激光笔做出相应的动作,从而控制红点向目标运动。
3. 运动控制理论及自动追踪方法
3.1 运动目标控制理论
3.1.1 题目解析
在基本要求的第二小问中,我们需要沿屏幕四边顺时针移动一周,且要求与边缘相差不超过2cm;在第三和第四问中,我们需要沿着A4纸一圈的黑色电工胶移动,并且与要尽可能的让红点不脱离黑胶带。在这几个问中,如果我们知道了A4纸和屏幕四周各个顶点的坐标,那么题目是不是就变成了点到点的移动,对于题目中所说的偏离黑线或者屏幕边沿会被扣分,如果我点到点的过程中走的直线并且这个直线够直,那么还会存在偏离黑线的情况吗?所以E题的基本要求就变成了如何走很直的直线。
3.1.2 如何走直线
对于步进电机云台来说,它每次的移动都来源于MCU给的脉冲,所以红点在屏幕上的移动就像是一步一步的阶梯式的。如下图所示。只要我们控制脉冲的数量,红点就能按我们想要的方向前进。
由于我们使用的驱动是DRV8825,它可以进行32分频,并且步进电机每一个步进值为1.8°,从理论上来说,MCU每发生一个脉冲,步进电机转动1.8/32 = 0.05625°。这一点弧度再乘以云台到屏幕的距离1m,算下来就是0.05625°/180 *PI *100 = 0.098174 cm,精度相当的高了。每一个步进值都不到0.1cm,当它运动起来时,完全可以看作是直线。
那么如何才能简单的让步进电机朝着我们想要的方向运动呢?
当我们想要让步进电机往37°方向运动时,通过计算,我们可以得出往x方向需要运动4步,往y方向需要运动3步,那么我们只需要往x轴的步进电机发出4个脉冲,往y轴的步进电机发送3个脉冲就可以了吗?虽然这样确实可以让红点停在我们37°得方向上,但是我们想要的不单是停在目标方向上,还要求轨迹是直线。在上图中当我们往45°方向运动时我们可以往x方向运动一步后接着往y轴运动一步,这样交错着来其轨迹正好就是一条朝着45°运动的直线。但是当我们想往37°方向运动时,该怎么分解x和y轴呢?
其实我们完全可以将一整个运动分解成x轴和y轴的两个运动,当x轴的速度和y轴的速度恒定且速度比为4:3时,这时的运动轨迹就是一条沿着37°的直线。而速度又可以由单位时间内运动的步数来表示。
那么云台运动的方向又是怎么确定的呢?通过读取openmv发来的红点坐标点与目标点x轴坐标和y轴坐标之差来确定我们的方向,而且我们没有必要把这两个差值计算成角度,我们可以直接将这两个差值作为上文提到的单位时间内运动的步数。
所以分析到这,我们得出的关键结论就是 计算红点和目标点的坐标差,将其作为单位时间内的云台的脉冲数。就能使红点往目标点移动的同时轨迹为直线。
4. 系统硬件设计
4.1 运动控制系统设计
本小组设计的目标运动控制系统的硬件原理框图如下:
4.1.1 MCU
本次使用CW32F030C8T6这款以Cortex M0+为内核的高性能处理器。
4.1.2 云台
云台的选型对于本项目的性能至关重要。市面上的二维云台主要有两种方案。
方案一:步进电机云台。
步进电机是将电脉冲信号,转变为角位移或线位移的开环控制电机,又称为脉冲电机。在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响。当步进驱动器接收到一个脉冲信号时,它就可以驱动步进电机按设定的方向转动一个固定的角度,称为“步距角”。 步进电机的旋转是以固定的角度一步一步运行的,可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的,同时可以通过控制脉冲频率,来控制电机转动的速度和加速度,从而达到调速的目的。
电机旋转的角度正比于脉冲数,具有较好的位置精度和运动重复性,每步的精度在百分之三到百分之五,而且不会将每一步的误差积累到下一步,很适合用作云台,但是市面上没有现成的步进电机云台,需要自己制作。
舵机云台
舵机(英文叫Servo):它由直流电机、减速齿轮组、传感器和控制电路组成的一套自动控制系统。通过发送信号,指定输出轴旋转角度。舵机一般而言都有最大旋转角度(比如180度。)与普通直流电机的区别主要在,直流电机是一圈圈转动的,舵机只能在一定角度内转动,不能一圈圈转。
舵机云台由于其控制简单,且成本较低,云台结构简单等优势,占据了市面上大部分市场,但是它的缺点就是有死区,控制精度较低。
综上所述,最终我们小组选择了使用28步进电机再配合Fusion360设计自己的二维步进电机云台。
5. OpenMV模块识别设计
5.1 运动控制系统中视觉识别的实现
根据任务要求,需要对屏幕边线、黑色矩形边框进行识别,一般市面上广泛采用OpenMV和K210作为视觉识别模块,具体方案如下:
方案一:Open MV4 H7 Plus
OpenMV 是一个开源,低成本,功能强大的机器视觉模块。该模块以 STM32H7CPU为核心,集成了 OV5654 摄像头芯片,在小巧的硬件模块上,用 C 语言高效地实现了核心机器视觉算法,提供 Python 编程接口,可以用 Python 语言使用 OpenMV提供的机器视觉功能。
方案二:K210
K210 是基于 RISC-V 精简指令集的一款 MCU,在众多特色中,芯片架构包含了一个自研的神经网络硬件加速器KPU 属于最大特色,可以高性能地进行卷积神经网络运算。在 MCU 的 AI 计算方面,K210 芯片的算力非常给力,根据嘉楠官网的描述,K210 的 KPU 算力能够达到 0.8TFLOPS。
由于本项目对视觉的要求不高,所以我们选择了对于我们较熟悉的openmv H7 PLUS,其MCU的参数如下:
6. 系统软件设计
6.1 运动控制系统程序设计
该运动目标控制系统使用状态机来实现在不同题目间切换,在不同的题目中,MCU通过串口发送不同的数据让OPENMV在识别矩形框和识别红点间切换。其程序流程图如下:
OpenMV的程序设计
根据OPENMV串口接收到的数据不同,发送不同的坐标信息给MCU,其程序框图如下:
6.2.1 串口初始化
uart = UART(3, 115200)
6.2.2 发送红点坐标
if uart.any():
char = uart.readchar() # read 1 character and returns it as an integer 读取一个字符,并返回其整数形式
if char == 0x30 + 1 :
print('11111111')
red_blobs = img.find_blobs(red_thresholds, area_threshold = 1, pixels_threshold = 1)
if red_blobs: # 如果找到红点
min_red_blob = find_max(red_blobs)
img.draw_cross(min_red_blob.cx(), min_red_blob.cy(), color=[0,255,0]) # cx, cy
x = struct.pack('i', min_red_blob.cx())
y = struct.pack('i', min_red_blob.cy())
z = x + y+'*#'
uart.write(z)
6.2.3 发送矩形坐标
elif char == 0x30 + 2:
print('22222222')
a = 1
while(a):
img = sensor.snapshot().lens_corr(strength = 1.0)
for r in img.find_rects(threshold = 10000):
z = bytes()
img.draw_rectangle(r.rect(), color = (255, 0, 0))
for p in r.corners():
img.draw_circle(p[0], p[1], 5, color = (0, 255, 0))
x = struct.pack('i', p[0])
y = struct.pack('i', p[1])
print(p[0],p[1])
z = z + x + y
a = a - 1
z = z + '*#'
uart.write(z)
7. 软件编写说明
7.1 运动控制系统文件构成
EWARM--编译生成的文件
HARDWARE---用户自己的外设库
Librarys---CW32库函数
MDK--keil5 工程目录
User---main函数
7.1.1 OLED
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WritePin(CW_GPIOB, GPIO_PIN_6,x)
#define OLED_W_SDA(x) GPIO_WritePin(CW_GPIOB, GPIO_PIN_7,x)
/*引脚初始化*/
void OLED_I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__RCC_GPIOB_CLK_ENABLE();
PB07_AFx_I2C1SDA();
PB06_AFx_I2C1SCL();
GPIO_InitStructure.IT = GPIO_IT_NONE; //不配置中断
GPIO_InitStructure.Pins = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
I2C_InitTypeDef I2C_InitStruct;
I2C_InitStruct.I2C_BaudEn = ENABLE;
I2C_InitStruct.I2C_Baud = 0x20; //500K=(64000000/(8*(1+1))
I2C_InitStruct.I2C_FLT = DISABLE;
I2C_InitStruct.I2C_AA = DISABLE;
I2C1_DeInit();
I2C_Master_Init(CW_I2C1,&I2C_InitStruct);//初始化模块
I2C_Cmd(CW_I2C1,ENABLE); //模块使能
}
7.1.2 按键
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.IT = GPIO_IT_NONE; //不配置中断
GPIO_InitStructure.Mode =GPIO_MODE_INPUT_PULLUP;
GPIO_InitStructure.Pins = KEY1_GPIO_PIN;
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.IT = GPIO_IT_NONE; //不配置中断
GPIO_InitStructure.Mode =GPIO_MODE_INPUT_PULLUP;
GPIO_InitStructure.Pins = KEY2_GPIO_PIN;
GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.IT = GPIO_IT_NONE; //不配置中断
GPIO_InitStructure.Mode =GPIO_MODE_INPUT_PULLUP;
GPIO_InitStructure.Pins = KEY3_GPIO_PIN;
GPIO_Init(KEY3_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.IT = GPIO_IT_NONE; //不配置中断
GPIO_InitStructure.Mode =GPIO_MODE_INPUT_PULLUP;
GPIO_InitStructure.Pins = KEY4_GPIO_PIN;
GPIO_Init(KEY4_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.IT = GPIO_IT_NONE; //不配置中断
GPIO_InitStructure.Mode =GPIO_MODE_INPUT_PULLUP;
GPIO_InitStructure.Pins = KEY5_GPIO_PIN;
GPIO_Init(KEY5_GPIO_PORT, &GPIO_InitStructure);
}
7.1.3 串口初始化及数据发送与接收
初始化部分
void user_uart_init()
{
DEBUG_USART_APBClkENx(DEBUG_USART_CLK, ENABLE);
user_uartgpioinit();
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = DEBUG_USART_BaudRate;
USART_InitStructure.USART_Over = USART_Over_16;
USART_InitStructure.USART_Source = USART_Source_PCLK;
USART_InitStructure.USART_UclkFreq = DEBUG_USART_UclkFreq;
USART_InitStructure.USART_StartBit = USART_StartBit_FE;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(DEBUG_USARTx, &USART_InitStructure);
USART_ITConfig(DEBUG_USARTx, USART_IT_RC, ENABLE);
//优先级,无优先级分组
// NVIC_SetPriority(DEBUG_USART_IRQ, 0);
//UARTx中断使能
__disable_irq();
NVIC_EnableIRQ(DEBUG_USART_IRQ);
__enable_irq();
}
串口数据发送
/**
* @brief 发送字符串
*
* @param USARTx :USARTx外设
* 参数可以是:
* CW_UART1、CW_UART2、CW_UART3
* @param String :待发送的字符串
*/
void USART_SendString(UART_TypeDef *USARTx, char *String)
{
while (*String != '\0')
{
USART_SendData_8bit(USARTx, *String);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
String++;
}
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXBUSY) == SET);
}
串口接收中断
void UART1_IRQHandler(void)
{
/* USER CODE BEGIN */
uint8_t Res; //定义unsigned char型字符Res
if (USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
{
USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
//接收中断(接收到的数据必须是0x0d 0x0a结尾)
//这里判断发送接收完成的依据就是串口数据0x0d 0x0a,
//0x0d是CR(carriage return)回车的意思,光标回到最左边,
//0x0a是LF(line feed)换行的意思,光标到达下一行,
//但是在PC上回车和换行是在一起的就是按下回车按键
//当然可以更改程序使用其他进行判断例如使用0x2a也就是*进行结束判断
Res =USART_ReceiveData_8bit(CW_UART1);//(USART1->DR);
if(Res == 0X2A)
{
//PC13_TOG();
}
//读取接收到的数据,存放到变量Res中
if((USART_RX_STA&0x8000)==0)
//判断接收是否未完成
//接收完成未清除标志位,还是会不断进入到接收中断,所以使用标志进行判断,
//当接收完成便不会跳入到判断,从而不执行任何指令,空等待
//使用条件判断是否已经接收完数据,这里判断接收完的依据就是收到了0x0a;
//具体判断在后面
{
if(USART_RX_STA&0x4000)
//如果接收到了0x0d,那么再进一步执行是否接收到0x0a的判断
{
if(Res!=0X23)USART_RX_STA=0; // 0x23 #
//没有接收到0x0a那么说明,数据未正确传输或者接收错误,重新开始判断,
//但是这里没有将接收到的数据进行清空,也没有退出接收中断,此程序只是从头开始执行接收判断
else USART_RX_STA|=0x8000;
//接收完成了,收到了0x0a那么标志位USART_RX_STA将会变成0x8000,将不再进行数据检测与存储
}
else
//还没收到0X0D,说明数据还未发送结束继续进行数据的检测与存储
{
if(Res==0X2A)USART_RX_STA|=0x4000; // 0X2A *
//收到了数据0x0d,标志位USART_RX_STA变成0x4000
else
{
//如果没有接收到数据0x0d,执行判断是否存储数组已满,已满则重新开始接收
USART_RX_BUF[USART_RX_STA&0X3FFF] = Res ;
//将接收到的数据写进数组,标志位USART_RX_STA与上0X3FFF清除前两位以防止标志位与8000和4000冲突
USART_RX_STA++;
//数组地址加一,向后排
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
//接收数据错误,超出数组大小,又开始接收向数组重新写
}
}
}
}
/* USER CODE END */
}
7.1.4 定时器初始化
定时器就是用来实现上文所说的单位时间的,并且在定时器中断中,实现了单位时间运动特定的步数。
使用GTIM1来作为X轴运动的定时器,使用GTIM2作为Y轴运动的定时器。使用BTIM2做为按键扫描的定时器。
void user_tim_init()
{
__RCC_GTIM1_CLK_ENABLE();
__RCC_GTIM2_CLK_ENABLE();
GTIM_InitTypeDef GTIM_InitStruct;
GTIM_InitStruct.Mode = GTIM_MODE_TIME;
GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV128;
GTIM_InitStruct.ReloadValue = 50000 - 1; //50ms 一次翻转,周期是1ms
GTIM_InitStruct.ToggleOutState = ENABLE;
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
GTIM_Cmd(CW_GTIM1, DISABLE);
// GTIM_InitStruct.Mode = GTIM_MODE_TIME;
// GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
// GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV64;
// GTIM_InitStruct.ReloadValue = 50000 - 1; //50ms 一次翻转,周期是1ms
// GTIM_InitStruct.ToggleOutState = ENABLE;
GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct);
GTIM_ITConfig(CW_GTIM2, GTIM_IT_OV, ENABLE);
GTIM_Cmd(CW_GTIM2, DISABLE);
BTIM_TimeBaseInitTypeDef BTIM_TimeBaseInitStruct;
BTIM_TimeBaseInitStruct.BTIM_Mode = BTIM_Mode_TIMER; // ms?
BTIM_TimeBaseInitStruct.BTIM_Period = 20000; // 20ms
BTIM_TimeBaseInitStruct.BTIM_Prescaler = BTIM_PRS_DIV64;
BTIM_TimeBaseInit(CW_BTIM2, &BTIM_TimeBaseInitStruct);
BTIM_ITConfig(CW_BTIM2, BTIM_IT_OV, ENABLE);
BTIM_Cmd(CW_BTIM2, ENABLE);
__disable_irq();
NVIC_EnableIRQ(GTIM1_IRQn);
NVIC_EnableIRQ(GTIM2_IRQn);
NVIC_EnableIRQ(BTIM2_IRQn);
__enable_irq();
}
7.1.5 主函数
int32_t main(void)
{
// GPIO_Configuration();
/* System Clocks Configuration */
RCC_Configuration();
InitTick(64000000ul); //初始化SysTick
/* GPIO Configuration */
GPIO_Configuration();
/* NVIC Configuration */
NVIC_Configuration();
key_init();
user_uart_init();
OLED_Init();
// OLED_ShowString(2,1,"hello");
user_tim_init();
motor_step_test();
// USART_SendString(CW_UART1, "\r\nCW32F030 UART Interrupt\r\n");
mode_select_1();
while (1)
{ // 状态机切换函数。
current_operation_index=table[func_index].current_operation;
(*current_operation_index)();//执行当前操作函数
}
}
8. 比赛心得与经验
今年电赛本科组没有单独的小车题,而我们前期基本上全都准备小车去了,云台都没准备,只是简单的画了个模型。对云台的控制没有一点准备。而且比赛要求准备两个云台,我们的步进电机数量不够,只够搭建一个云台,并且我们准备的红色激光功率太低。在黑线上的识别效果不好。好在顺丰速度够快,我们还是在比赛结束前顺利的完成了基础部分。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_41781494/article/details/134664724
|
|