OpenMV与单片机通信总结

文章目录

  • 前言
  • 一、通信原理
  • 传输方式
  • 关于int整型
  • 还有一个更简便的方法
  • 二、openmv与32通信
  • openmv发 32收
  • 大端与小端
  • 浮点型的收发
  • openmv收32发
  • 三、openmv与arduino通信
  • 四、 US100(超声波)和32通信
  • 五、蓝牙和32通信
  • 六、zigbee两车通信
  • 总结

  • 前言

    总结一下串口通信的用法和实战


    一、通信原理

    传输方式

    传输分为两种形式: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的通信下次专门讲。

    物联沃分享整理
    物联沃-IOTWORD物联网 » OpenMV与单片机通信总结

    发表评论