OpenMv和STM32通信
OpenMv和STM32通信问题
前言
最近一段时间都在捣鼓OpenMV和Stm32的通信问题,刚开始不知道哪里出了问题,一直通信失败,明明使用TTL串口接收OpenMv发送的数据是可以在串口调试助手上显示的,但就是无法发给Stm32的USART串口。经过了差不多一周的时间,终于解决了。于是在这里记录学习记录。
一、OpenMv配置
1.第一种发送方法
OpenMv代码如下
# Untitled - By: 86188 - 周二 5月 25 2021
import sensor, image, time,pyb
from pyb import UART,LED
import json
import ustruct
#white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值
red_threshold_01 = ((2, 51, 11, 127, -128, 127)) #红色阈值
LED_R = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.
LED_G = pyb.LED(2)
LED_B = pyb.LED(3)
LED_R.on()
LED_G.on()
LED_B.on()
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()
LED_R.off()
LED_G.off()
LED_B.off()
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 send_data_packet(x,y,z,w):
temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
0x2C, #帧头1
0x12, #帧头2
int(x), # up sample by 2 #数据1
int(y), # up sample by 2 #数据2
int(z),
int(w))
#0x5B)
uart.write(temp); #串口发送
while(True):
img = sensor.snapshot()
#time.sleep_ms(1000)
#send_data_packet(x,y)
blobs = img.find_blobs([red_threshold_01]);
cx=0;cy=0;
if blobs:
#如果找到了目标颜色
max_b = find_max(blobs);
# Draw a rect around the blob.
img.draw_rectangle(max_b[0:4]) # rect
#用矩形标记出目标颜色区域
img.draw_cross(max_b[5], max_b[6]) # cx, cy
#img.draw_cross(160, 120) # 在中心点画标记
#在目标颜色区域的中心画十字形标记
cx=max_b[5];
cy=max_b[6];
cw=max_b[2];
ch=max_b[3];
#data = bytearray([x,y])
send_data_packet(cx,cy,cw,ch)
#time.sleep_ms(1000)
代码作用:OpenMv使用的是python语言编写,而这段代码的主要目的就是在openmv视野种寻找红色色块,并将其中点坐标发送会Stm32,并在OLED屏幕上显示。
主要通信函数如下
def send_data_packet(x,y,z,w):
temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
0x2C, #帧头1
0x12, #帧头2
int(x), # up sample by 2 #数据1
int(y), # up sample by 2 #数据2
int(z),
int(w))
#0x5B)
uart.write(temp); #串口发送
这里使用了数据包的形式发送数据,将一帧的数据包装,并发送给Stm32,此数据包中的包头非常重要,也就是0x2C以及0x12,这两个数据便与Stm32接收中断中进行判断,以确保数据的正确性。
对于数据包格式,此等的使用规划:
#pack各字母对应类型
#x pad byte no value 1
#c char string of length 1 1
#b signed char integer 1
#B unsigned char integer 1
#? _Bool bool 1
#h short integer 2
#H unsigned short integer 2
#i int integer 4
#I unsigned int integer or long 4
#l long integer 4
#L unsigned long long 4
#q long long long 8
#Q unsilong long long 8
#f float float 4
#d double float 8
#s char[] string 1
#p char[] string 1
#P void * long
对于此处我所使用的"<bbhhhh",
第一个第二个数据为包头,便是b,字符串类型。而后面开始为数据格式,我选择的是h也就是短整型,传输给Stm32的时候,便是两个字节的数据格式。
如图,一串完整的数据便是 2C 12 36 00 80 00 2E 00 9D 00。
2.第二种发送方法
除了以上这种以包(pack)的方式发送给stm32外,还可以使用另外的一种方法发送:
def send_data(cx,cy,w,h):
datalist = [ 0x2C,0x12,cx,cy,w,h]
datalist.append(sum_checkout(datalist))
data = bytearray(datalist)
return data
#检验数据和
def sum_checkout(data_list):
data_sum = 0
for temp in data_list:
data_sum = data_sum + temp
return data_sum
这段代码和之前不同的是:我将0x5B 最后一位的数据校验位改成了用sum_checkout() 这段函数求和取余的方法作为该数据的数据校验位。
但是使用这样不封包的方法发送,就必须加上data = bytearray(datalist),将数据转化,才可以进行通讯。
二、Stm32配置
Stm32的USART1配置如下:
u16 USART_RX_STA=0; //接收状态标记
void uart1_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
相比于平常配置并未存在较大差别,而实现通信的重要步骤是在USART1的串口接收中断中。
void USART1_IRQHandler(void)
{
u8 Res;
static u8 Rebuf[20]={0};
static u8 i = 0;
if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET )
{
Rebuf[i++] = USART_ReceiveData(USART1);
if(Rebuf[0] != 0x2c)
i = 0;
if((i==2)&&Rebuf[1] != 0x12)
i = 0;
if(i>=10)
{
memcpy(OpenMV_Rx_BUF,Rebuf,i);
i = 0;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志
}
}
其中定义了一个OpenMV_Rx_BUF[20]的数组来接收openmv发送过来的数据,需要使用extern修饰这个变量,以便于事项后续操作。
同时memcpy()函数也是十分重要的一个内置函数,可以实现两个数组的拷贝任务。
主函数的代码如下:
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart1_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
OLED_Init();
while(1)
{
LED0=!LED0;
delay_ms(200);
OLED_Refresh_Gram();
OLED_ShowNumber(0,20,OpenMV_Rx_BUF[2],3,12);
OLED_ShowNumber(20,20,OpenMV_Rx_BUF[4],3,12);
OLED_ShowNumber(40,20,OpenMV_Rx_BUF[6],3,12);
OLED_ShowNumber(60,20,OpenMV_Rx_BUF[8],3,12);
}
}
前面就提到了,由于我数据包的格式设置问题,存入OpenMV_Rx_BUF[] 数组中的数据,真正有效的是第3、5、7、9位(因为选择了h类型数据格式,一个数据占2位)。
总结
博主我之前数据一直发送失败的原因是,博主使用了 uart.write() 这个函数,企图通过 uart.write() 这个函数实现数据发送。
这个函数在实现OpenMv和PC端的通信上没有问题,可以将数据打在串口调试助手上,但在其与Stm32的通信问题上就存在问题。
若要使用 uart.write() 实现与stm32的通信,便要使用bytearray()函数将其转化,才可以进行通信。
FH = bytearray([0x2C,0x12])
uart.write(FH)