OpenMV与单片机通信总结
文章目录
前言
总结一下串口通信的用法和实战
一、通信原理
传输方式
传输分为两种形式:ascii码和16进制,选择ascii发送时,发送1为字符‘1’,选择16进制发送1时,为整型1。
其实几乎所有通讯底层传输的都是二进制流,为了方便显示和查看,显示为十六进制,传输的是不是ascii码不是串口要关心的问题,串口所在的层面只要负责把你的二进制流传输过去,你可以用它传输ascii码,也可以传输二进制流,二进制流就是一串数字,比如你发送字符’K’,你代码里写’K’,但是只是你看到的是K,电脑始终认为它是0x4B,然后你用串口发送0x4B,串口会先把它转换成二进制01001011,然后按照这个顺序发送高低电平,所以ascii码不是串口要关心的,你的这个字符在电脑里从来都是用一个8bit数字表示的。
关于int整型
整型int32必须拆成四个int8发送,因为串口默认一个pack可以携带8bit有效数据,比如(int32)1234567890要拆成(int8) {0x49,0x96,0x02,0xD2}按顺序发送,然后接收到的是(int8){0x49,0x96,0x02,0xD2}再换算回(int32)1234567890,就是先发0x49,然后发0x96,然后0x02,然后0xD2。
接收方收完四个之后自己组合回去。
还有一个更简便的方法
用结构体和指针,先定义一个结构体,包含你要发的数据类型
然后填入你要发送的实际数据,获取指向这个结构体实例的指针
然后读出这个指针指向的这块内存里的数据,直接就是一堆int8,接收方收到之后先用同样的结构体定义开辟一块内存,然后直接把收到的数据写入这块内存,就能从结构体里面直接读出各个变量的值了,这样一次可以发好多,而且简单。不过接收方也要定义相应的结构体
二、openmv与32通信
openmv发 32收
主要参考
次要参考
官方openmv.uart讲解
主要是用到了ustruct.pack这个函数,以字节流的方式通信。
openmv方 (openmv P4 USART3_TX P5 USART3_RX)
导库
from pyb import UART
import ustruct ##micropy 中的库
初始化串口
uart = UART(3,115200) #定义串口3变量 #P4 P5
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
# 8位数据位 无奇偶校验 1位停止位
定义发送
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("<bbii", #格式为俩个字符俩个短整型(2字节) #"<bbhhb"
0xAA, #帧头1
0xAE, #帧头2
int(cx), # up sample by 4 #数据1
int(cy), # up sample by 4 #数据2
int
) #0x5B
uart.write(data) #必须要传入一个字节数组
定义了一个结构体
class singleline_check():
rho_err = 0
theta_err = 0
state = 0
发送
sending_data(singleline.rho_err,singleline.theta_err); #发送点位坐标
stm32方(f103c8t6)( PA9 USART1_TX PA10 USART1_RX)
//P4接PA10
//P5接PA9
初始化串口
void usart_init(u32 bound)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
GPIO_InitTypeDef GPIO_InitStructure;//GPIO端口设置
//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
NVIC_InitTypeDef NVIC_InitStructure;//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_InitTypeDef USART_InitStructure;//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
}
中断函数
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Data;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Data =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
Openmv_Receive_Data(Data);
}
}
接收数据 校验帧头,这里用u8定义一个数组接收,因为++_data_cnt这里先自增了,所以从RxBuffer[1]开始收数据。
void Openmv_Receive_Data(int16_t data)//接收Openmv传过来的数据
{
/* 局部静态变量:接收缓存 */
static u8 RxBuffer[10];
/* 数据长度 */ /* 数据数组下标 */
static u8 _data_cnt = 0;
/* 接收状态 */
static u8 state = 0;
/* 帧头1 */
if(state==0&&data==0xAA)
{
state=1;
}
/* 帧头2 */
else if(state==1&&data==0xAE)
{
state=2;
_data_cnt = 0;//这里数据下标置0
}
/* 接收数据租 */
else if(state==2)
{
RxBuffer[++_data_cnt]=data; //++a 立即以自增值计算
if(_data_cnt>=8 )
{
state = 0;
Data_Processing(RxBuffer,_data_cnt);
}
}
/* 若有错误重新等待接收帧头 */
else
state = 0;
}
处理整型int32,例如发1234567890 =hex(499602D2)
先发0x49,然后发0x96,然后0x02,然后0xD2
首先要记住:读数据永远是从低地址开始的!!!
然后读到的D2放到RxBuffer[1]里,
02放到RxBuffer[2]里,
96放到RxBuffer[3]里,
49放到RxBuffer[4]里。
大端与小端
数据存储:小端模式和大端模式——终于搞明白了!!!
因为stm32是小端模式,即存放内存时就是低位对应低地址,高位对应高地址,所以再将读到的低地址的数放到低地址,高地址的数放到高地址,就能还原得到的整型了。
void Data_Processing(u8 *data_buf,u8 num)
{
// 一个字节8位 int型4个字节
// 读取偏移角度原始数据
theta_org = (int)(*(data_buf+1)<<0) | (int)(*(data_buf+2)<<8) | (int)(*(data_buf+3)<<16) | (int)(*(data_buf+4)<<24) ;
theta_err = theta_org;
// 读取偏移尺寸原始数据
rho_org = (int)(*(data_buf+5)<<0) | (int)(*(data_buf+6)<<8) | (int)(*(data_buf+7)<<16) | (int)(*(data_buf+8)<<24) ;
rho_err = rho_org;
}
浮点型的收发
浮点型的收发和整型的应该没什么不一样。浮点型收发
网上看到其他人用sscanf来接发浮点型,不过看之前博客说这种方式不稳定,就没采用。
#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
openmv收32发
这里只测试了一个字符的收发
openmv部分
接收函数
def receive_data():
global uart
if uart.any():
tmp_data = uart.read(); #读取所有可用字符#b'a'
print(tmp_data)
return tmp_data
receive_data()
32部分
发送函数
void Usart1SendByte(char byte)//发送一个字节 硬件连接选用的为USART1
{
USART1->SR; //不加上这句可能会导致奇奇怪怪的问题
USART_SendData(USART1, (uint8_t) byte);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
}
//测试发函数 只发一次
if (shuzi ==4 &&state ==0)
{
state = 1;
USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
Usart1SendByte('a');
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
}
最后openmv上接收为b'a'
三、openmv与arduino通信
前面配置一样,不过这里没用struct,默认为# 8位数据位 无奇偶校验 1位停止位
openmv部分发
if (state>=2 && a=1 ): ##只发一次
uart.write('C')
a = 0
arduino部分
在电脑上显示读到的字符
Serial.begin(115200); // Starting Serial Terminal
void openmv_Init(void)
{
Serial3.begin(115200);
}
void fanyin(void)
{
if(Serial3.available()) //if number of bytes (characters) available for reading from {
{
char c = Serial3.read(); //read a character from Serial3
Serial.print(c); //print the character to Serial
Serial3.write(Serial3.read()); //send what you read
}
}
vscode上串口助手显示C
四、 US100(超声波)和32通信
特别要注意的一点就是US100是RX接RX,TX接TX,这个和一般不同!!!
US-100超声波测距模块 – 原理与数据获取的编程实现
插上跳线帽,用的是串口模式
us100.c
用了rct6的脚,只要是串口中断的两个脚都一样
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
//PC12 uart5_tx
//PD2 uart5_rx
//波特率9600 起始位1位 停止位1位 数据位8位 无奇偶校验
//2cm-450cm
//US100 Tx-Tx Rx-Rx
//跳线帽接上是串口模式 不接上是电平触发模式
//tx-外部电路Ttig端
//rx-外部电路Echo端
u8 flag;
u8 UART5_RX_BUF[5];
float i = 0;
void us100_init(u32 bound)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE); // 使能USART4 时钟
GPIO_InitTypeDef GPIO_InitStructure;//GPIO端口设置
//USART1_TX GPIOC12
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOA.9
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); //使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE); // 使能USART4 时钟
//USART1_RX GPIOD2
GPIO_InitTypeDef GPIO_InitStruct;//GPIO端口设置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOD, &GPIO_InitStruct);//
NVIC_InitTypeDef NVIC_InitStructure;//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
USART_InitTypeDef USART_InitStructure;//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(UART5, &USART_InitStructure); //初始化串口1
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(UART5, ENABLE); //使能串口4
}
void UART5_IRQHandler(void)
{
u8 res;//,i;
if(USART_GetITStatus(UART5,USART_IT_RXNE) != RESET)
{
res = USART_ReceiveData(UART5);
if(flag != 2)
{
UART5_RX_BUF[flag] = res;
flag ++;
}
}
}
void US100_send_byte(char data)
{
while(USART_GetFlagStatus(UART5,USART_FLAG_TXE) == RESET);
USART_SendData(UART5,data);
}
//测距
void dis(void)
{
US100_send_byte(0x55); //给超声波发送0x55获得距离
delay_ms(50);//必须给50ms的超声波算距离时间
i = (UART5_RX_BUF[0] *256)+ UART5_RX_BUF[1];
if(flag == 2)
flag = 0;
printf("DIS:%fcm\r\n",i/10.0);
delay_ms(1000);
}
us100.h 将extern 写在.h里以后在主函数include就行,就不用再定义了
#ifndef __US100_H
#define __US100_H
void us100_init(u32 bound);
extern u8 flag;
extern u8 USART6_RX_BUF[5];
void US100_send_byte(char data);
void dis(void);
#endif
main.c
while(1)
{dis();}
就能获得数据
五、蓝牙和32通信
这里几乎就和openmv与32通信一样了,这里只是换了一个串口,然后在手机上的蓝牙串口APP,以ascii码的形式发送1,也就是字符1,然后灯点亮。
//蓝牙串口bt08b RCT6 PC10 UART4_TX
// PC11 UART4_RX
//默认pin码为1234 波特率为9600 从角色
/*
USART:通用同步和异步收发器
UART:通用异步收发器
当进行异步通信时,这两者是没有区别的。区别在于USART比UART多了同步通信功能。
这个同步通信功能可以把USART当做SPI来用,比如用USART来驱动SPI设备和IIC设备
*/
void uart_bt_init(u32 bound)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE); // 使能USART4 时钟
GPIO_InitTypeDef GPIO_InitStructure;//GPIO端口设置
//USART1_TX GPIOC10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOC11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOA.10
NVIC_InitTypeDef NVIC_InitStructure;//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
USART_InitTypeDef USART_InitStructure;//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(UART4, &USART_InitStructure); //初始化串口1
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(UART4, ENABLE); //使能串口4
}
void UART4_IRQHandler(void) //串口1中断服务程序
{
u8 Data;
if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Data =USART_ReceiveData(UART4);//(USART1->DR); //读取接收到的数据
//Openmv_Receive_Data(Data);
receive_data(Data);
}
}
void receive_data(int16_t data)
{
static u8 RxBuffer[10];
static u8 _data_cnt = 0;
//static u8 state = 0;
_data_cnt = 0;//这里数据下标置0
RxBuffer[++_data_cnt]=data; //++a 立即以自增值计算
if(_data_cnt>=1 ) //只在[1]里有数据
{
if (RxBuffer[1] == '1')
LED1_ON();
}
}
六、zigbee两车通信
ZigBee模块学习
用法很简单,将两个模块rx,tx分别插到两个单片机的串口,就相当于有一条无线的线将他们连在了一起,一个设为A端,一个设为B端,频道和波特率设为一致,然后A发B收用串口固定的语句就行。
比方说arduino的发Serial2.write('C');
arduino的收
if(Serial3.available()) //if number of bytes (characters) available for reading from {
{
char c = Serial3.read(); //read a character from Serial3
}
然后执行想要的操作就行。
总结
这是在电赛时和之前学会的一些东西,将它总结了一下,还有esp8266和32的通信下次专门讲。