把代码写到一起综合一下
时间: 2025-07-23 12:49:41 浏览: 2
<think>我们正在整合K210视觉识别模块与STM32F103C8T6的串口通信,实现颜色方块识别和云台控制。根据用户要求,K210使用UART2(TX=24, RX=25),STM32使用USART1(PB3为RX,PB4为TX)。下面将提供完整的代码整合方案。
### 1. K210端完整代码(MaixPy)
```python
import sensor, image, time, lcd
from machine import UART
# 初始化摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320x240
sensor.set_auto_gain(False) # 关闭自动增益
sensor.set_auto_whitebal(False) # 关闭白平衡
sensor.set_vflip(True) # 根据摄像头安装方向调整
sensor.set_hmirror(True)
sensor.skip_frames(time=1000) # 让摄像头稳定
# 初始化LCD(可选)
try:
lcd.init()
except:
pass
# 配置UART2 (TX=IO24, RX=IO25)
uart = UART(UART.UART2, 115200, bits=8, parity=None, stop=1)
# 红色方块LAB阈值(需根据实际环境校准)
RED_THRESHOLD = (30, 70, 10, 70, 10, 50)
def send_data(dx, dy):
"""发送数据协议: 帧头(0xAA,0xBB) + 数据 + 校验和 + 帧尾(0x0D,0x0A)"""
# 将dx, dy转换为两个字节的有符号整数(大端序)
data_bytes = bytearray([(dx >> 8) & 0xFF, dx & 0xFF, (dy >> 8) & 0xFF, dy & 0xFF])
# 计算校验和(异或校验)
checksum = 0
for b in data_bytes:
checksum ^= b
# 构建完整数据包
packet = bytearray([0xAA, 0xBB]) + data_bytes + bytearray([checksum, 0x0D, 0x0A])
uart.write(packet)
while True:
start_time = time.ticks_ms()
img = sensor.snapshot()
blobs = img.find_blobs([RED_THRESHOLD], pixels_threshold=100, area_threshold=100, merge=True)
if blobs:
# 找到最大的色块
max_blob = max(blobs, key=lambda b: b.area())
# 在图像上标记
img.draw_rectangle(max_blob.rect(), color=(0,255,0))
img.draw_cross(max_blob.cx(), max_blob.cy(), color=(0,255,0))
# 计算中心坐标偏差(相对于图像中心)
dx = max_blob.cx() - img.width()//2
dy = max_blob.cy() - img.height()//2
send_data(dx, dy)
else:
send_data(0, 0) # 未检测到目标
# 显示帧率(可选)
fps = 1000 / (time.ticks_ms() - start_time)
img.draw_string(10,10, "FPS:%.1f" % fps, color=(255,0,0))
try:
lcd.display(img)
except:
pass
time.sleep_ms(10) # 控制循环频率
```
### 2. STM32端完整代码(基于HAL库)
#### 2.1 串口配置(USART1,PB3-RX,PB4-TX)
使用STM32CubeMX配置:
1. 禁用JTAG(在SYS->Debug选择Serial Wire)
2. 配置USART1:
- Mode: Asynchronous
- Baud Rate: 115200
- Word Length: 8 bits
- Parity: None
- Stop Bits: 1
- 启用串口全局中断
3. 配置PB3为USART1_RX(复用推挽上拉),PB4为USART1_TX(复用推挽上拉)
4. 配置TIM3的CH3(PB0)和CH4(PB1)为PWM输出(周期20ms,即50Hz)
#### 2.2 串口接收处理(带校验)
```c
// 在main.c中添加以下代码
#define PACKET_SIZE 9 // 帧头2+数据4+校验和1+帧尾2
uint8_t rx_buf[PACKET_SIZE];
uint8_t rx_index = 0;
int16_t target_x = 0, target_y = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if(huart->Instance == USART1) {
uint8_t data = (uint8_t)(huart->Instance->DR & 0xFF);
static uint8_t state = 0;
// 状态机解析
switch(state) {
case 0: // 等待帧头1
if(data == 0xAA) {
rx_index = 0;
rx_buf[rx_index++] = data;
state = 1;
}
break;
case 1: // 等待帧头2
if(data == 0xBB) {
rx_buf[rx_index++] = data;
state = 2;
} else {
state = 0;
}
break;
case 2: // 接收数据
rx_buf[rx_index++] = data;
if(rx_index >= PACKET_SIZE - 2) { // 收到完整数据包(不含帧尾)
// 检查帧尾
if(rx_buf[PACKET_SIZE-2] == 0x0D && rx_buf[PACKET_SIZE-1] == 0x0A) {
// 校验和验证
uint8_t checksum = 0;
for(int i=2; i<=5; i++) checksum ^= rx_buf[i];
if(checksum == rx_buf[6]) {
// 解析坐标
target_x = (rx_buf[2] << 8) | rx_buf[3];
target_y = (rx_buf[4] << 8) | rx_buf[5];
// 更新云台(PID控制可在此实现)
update_servos(target_x, target_y);
}
}
state = 0;
}
break;
default:
state = 0;
}
HAL_UART_Receive_IT(&huart1, &data, 1); // 重新开启接收中断
}
}
// 在main函数中启动串口接收中断
int main(void) {
// ... 初始化代码
uint8_t data;
HAL_UART_Receive_IT(&huart1, &data, 1);
while(1) {
// 主循环
}
}
```
#### 2.3 云台PWM控制函数
```c
// 舵机脉冲宽度限制(单位:微秒对应的计数值)
#define SERVO_MIN 1000 // 0.5ms (实际舵机可能不同)
#define SERVO_MID 1500 // 1.5ms
#define SERVO_MAX 2000 // 2.0ms
void update_servos(int16_t dx, int16_t dy) {
// 水平舵机(TIM3 CH3 -> PB0)
int32_t pulse_h = SERVO_MID + dx * 0.5; // 比例系数根据实际调整
pulse_h = (pulse_h < SERVO_MIN) ? SERVO_MIN : (pulse_h > SERVO_MAX) ? SERVO_MAX : pulse_h;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, pulse_h);
// 垂直舵机(TIM3 CH4 -> PB1)
int32_t pulse_v = SERVO_MID - dy * 0.5; // 注意方向,根据实际调整
pulse_v = (pulse_v < SERVO_MIN) ? SERVO_MIN : (pulse_v > SERVO_MAX) ? SERVO_MAX : pulse_v;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, pulse_v);
}
```
### 3. 调试建议
1. **颜色阈值校准**:使用K210 IDE的阈值编辑器,将红色方块放在不同光照条件下调整LAB阈值[^2]。
2. **通信测试**:先用串口调试助手验证K210发送的数据格式是否正确。
3. **舵机校准**:
- 确保舵机中位(1500μs)对应云台中心位置
- 机械限位避免舵机堵转
4. **抗干扰处理**:
- 在K210端添加形态学滤波(如`erode`)减少噪声干扰
- STM32端添加超时复位机制(超过100ms无数据将状态机复位)
### 4. 注意事项
1. K210与STM32必须共地连接
2. 舵机电源需独立供电(避免电流倒灌影响MCU)
3. 首次测试时去掉云台负载,防止意外转动造成损坏
> 完整工程代码参考:
> K210示例:https://gitee.com/AD123zsg/electronic-game-code/tree/master/K210
> STM32示例:https://pan.baidu.com/s/1XE36snBNJPciFRB0k57X0g?pwd=8cnl [^1]
§§相关问题§§
1. 如何校准K210的颜色识别阈值?
2. 二维云台舵机的PID控制如何实现?
3. 串口通信中如何添加数据校验?</think>### K210与STM32F103C8T6完整通信整合方案
#### 1. 系统架构
```mermaid
graph TD
A[K210视觉模块] -->|TX-24→PB3| B[STM32F103C8T6]
B -->|PB4→RX-25| A
B -->|TIM3_CH3/PB0| C[水平舵机]
B -->|TIM3_CH4/PB1| D[垂直舵机]
```
#### 2. K210端完整代码 (MaixPy)
```python
import sensor, image, time
from machine import UART, Timer
# 摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320x240
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
sensor.skip_frames(30)
# UART2配置 (TX=24, RX=25)
uart = UART(UART.UART2, 115200, bits=8, parity=None, stop=1)
# 红色方块LAB阈值 (需根据实际环境校准)
RED_THRESHOLD = (30, 70, 10, 70, 10, 50)
def send_data(dx, dy):
"""发送数据协议: 帧头(0xAA,0xBB) + 数据 + 校验和 + 帧尾(0x0D,0x0A)"""
# 打包数据 (有符号16位整数)
dx_bytes = dx.to_bytes(2, 'big', signed=True)
dy_bytes = dy.to_bytes(2, 'big', signed=True)
# 计算校验和 (异或校验)
checksum = 0
for b in dx_bytes: checksum ^= b
for b in dy_bytes: checksum ^= b
# 构建完整数据包
packet = bytearray([0xAA, 0xBB]) + dx_bytes + dy_bytes
packet.append(checksum)
packet.extend([0x0D, 0x0A])
uart.write(packet)
# 主循环
while True:
img = sensor.snapshot()
blobs = img.find_blobs([RED_THRESHOLD],
pixels_threshold=100,
area_threshold=100,
merge=True)
if blobs:
# 找到最大色块
max_blob = max(blobs, key=lambda b: b.area())
img.draw_rectangle(max_blob.rect(), color=(0,255,0))
# 计算中心坐标偏差 (范围: -160~160)
dx = max_blob.cx() - img.width()//2
dy = max_blob.cy() - img.height()//2
send_data(dx, dy)
else:
# 未检测到目标时发送特定值
send_data(0x7FFF, 0x7FFF)
time.sleep_ms(20) # 50Hz更新率
```
#### 3. STM32端完整代码 (HAL库)
```c
/* 在main.c中添加以下代码 */
// 1. 串口配置 (USART1 PB3-RX, PB4-TX)
void MX_USART1_UART_Init(void) {
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
// 启用接收中断
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
}
// 2. 引脚重映射配置 (禁用JTAG)
void SystemClock_Config(void) {
// ... 其他时钟配置
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_SWJ_DISABLE(); // 完全禁用JTAG
__HAL_AFIO_REMAP_USART1_ENABLE(); // 启用USART1重映射
}
// 3. 全局变量定义
#define PACKET_SIZE 9 // 帧头2+数据4+校验1+帧尾2
uint8_t rx_buf[PACKET_SIZE];
uint8_t rx_index = 0;
int16_t target_x = 0, target_y = 0;
// 4. 串口中断处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
static uint8_t state = 0;
uint8_t data = rx_data; // 接收到的字节
switch(state) {
case 0: // 等待帧头1
if(data == 0xAA) {
rx_index = 0;
rx_buf[rx_index++] = data;
state = 1;
}
break;
case 1: // 等待帧头2
if(data == 0xBB) {
rx_buf[rx_index++] = data;
state = 2;
} else {
state = 0;
}
break;
case 2: // 接收数据
rx_buf[rx_index++] = data;
if(rx_index >= PACKET_SIZE) {
// 验证帧尾
if(rx_buf[PACKET_SIZE-2] == 0x0D && rx_buf[PACKET_SIZE-1] == 0x0A) {
// 计算校验和
uint8_t checksum = 0;
for(int i=2; i<=5; i++) checksum ^= rx_buf[i];
// 校验通过则解析数据
if(checksum == rx_buf[6]) {
target_x = (rx_buf[2] << 8) | rx_buf[3];
target_y = (rx_buf[4] << 8) | rx_buf[5];
update_servos(target_x, target_y);
}
}
state = 0;
}
break;
}
// 重新启用接收中断
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
}
}
// 5. 舵机控制函数
void update_servos(int16_t dx, int16_t dy) {
// 水平舵机 (PB0/TIM3_CH3)
int32_t pulse_h = 1500 + (dx * 0.5); // 比例系数需校准
pulse_h = (pulse_h < 1000) ? 1000 : (pulse_h > 2000) ? 2000 : pulse_h;
TIM3->CCR3 = pulse_h;
// 垂直舵机 (PB1/TIM3_CH4)
int32_t pulse_v = 1500 - (dy * 0.5); // 注意Y轴方向
pulse_v = (pulse_v < 1000) ? 1000 : (pulse_v > 2000) ? 2000 : pulse_v;
TIM3->CCR4 = pulse_v;
}
```
#### 4. STM32CubeMX配置步骤
1. **USART1配置**:
- Mode: Asynchronous
- Baud Rate: 115200
- Word Length: 8 Bits
- Parity: None
- 启用中断
2. **引脚重映射**:
- PB3: USART1_RX (Remap)
- PB4: USART1_TX (Remap)
- PB0: TIM3_CH3
- PB1: TIM3_CH4
3. **TIM3配置**:
- Prescaler: 72-1 (1MHz时钟)
- Counter Period: 20000-1 (20ms周期)
- PWM Mode: PWM Mode 1
- CH3/CH4: Enable
#### 5. 调试技巧
1. **颜色校准**:
```python
# K210阈值编辑器
import sensor
sensor.set_windowing((240, 240))
sensor.run(1)
while True:
img = sensor.snapshot()
# 按方向键调整阈值
```
2. **通信测试**:
- K210端添加调试输出:`print("Sending:", dx, dy)`
- STM32端用LED指示数据接收:
```c
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 每次收到数据翻转LED
```
3. **运动平滑处理**:
```c
// 添加低通滤波
static float filtered_x = 1500, filtered_y = 1500;
filtered_x = filtered_x * 0.8 + pulse_h * 0.2;
filtered_y = filtered_y * 0.8 + pulse_v * 0.2;
TIM3->CCR3 = (uint16_t)filtered_x;
TIM3->CCR4 = (uint16_t)filtered_y;
```
> **重要提示**:
> 1. 首次测试时断开舵机连接,用万用表验证PWM信号
> 2. STM32烧录前禁用JTAG:`__HAL_AFIO_REMAP_SWJ_DISABLE();`
> 3. 完整工程参考:[STM32与K210通信示例](https://gitee.com/AD123zsg/electronic-game-code) [^3]
阅读全文
相关推荐



















