精华内容
下载资源
问答
  • 学习笔记:openmv与STM32串口传输数据的解析程序

    千次阅读 多人点赞 2019-10-19 21:37:08
    学习笔记:openmv与STM32串口传输数据的解析程序 学习初衷 大四毕业设计需要用到openmv与STM32之间的数据传输 由于之前没有学过Python,所以在openmvide编写程序时遇到很多问题。但是不得不说Python是真的好用...

    学习笔记:openmv与STM32串口传输数据的解析程序

    学习初衷

    大四毕业设计需要用到openmv与STM32之间的数据传输
    由于之前没有学过Python,所以在openmvide编写程序时遇到很多问题。但是不得不说Python是真的好用啊!开始时把这个问题想的很简单,因为之前也做过单片机之间的数据传输解析,可能是自身实力太差的原因,在借鉴其他博主的文章之后搞了两天才算是有些眉目。第一次写csdn的博客,有什么没表述清楚的,也请见谅,希望可以给到你一些帮助,欢迎大家一起学习和交流。

    Openmv代码部分

    这是一个识别最大圆并获取最大圆与中心坐标的代码
    最初的方案是采用字符的方式从openmv发送给STM32,但经过多次试验后,解析不成功,而且很容易掉帧
    但是在python中我没有学过怎么用串口发送16进制数。
    在查阅资料后发现bytearray()这个函数在openmv中能直接输出一个16进制字节数组
    然后通过串口可以发送。以下是openmv ide中的代码。

    
    import sensor, image, time
    from pyb import UART
    import json
    
    red_threshold  = (87, 21, 27, 93, -5, 92)
    sensor.reset() # Initialize the camera sensor.
    sensor.set_pixformat(sensor.RGB565) # use RGB565.
    sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
    sensor.skip_frames(10) # Let new settings take affect.
    sensor.set_auto_whitebal(False) # turn this off.
    clock = time.clock() # Tracks FPS.
    
    uart = UART(3, 115200)
    uart.init(115200, bits=8, parity=None, stop=1)  #8位数据位,无校验位,1位停止位
    
    def find_max(blobs):
        max_size=0
        for blob in blobs:
            if blob[2]*blob[3] > max_size:
                max_blob=blob
                max_size = blob[2]*blob[3]
        return max_blob
    while(True):
        clock.tick() # Track elapsed milliseconds between snapshots().
        img = sensor.snapshot() # Take a picture and return the image.
    
        blobs = img.find_blobs([red_threshold])
        if blobs:
    
            max_blob = find_max(blobs)
            img.draw_cross(max_blob.cx(),max_blob.cy())
            img.draw_circle(max_blob.cx(),max_blob.cy(),max_blob.cx()-max_blob.x(), color = (255, 255, 255))
            X =int(max_blob.cx()-img.width()/2)
            Y =int(max_blob.cy()-img.height()/2)
            
          #  FH = bytearray([0xb3,0xb3])
          #  uart.write(FH)     #打印帧头
    
            data = bytearray([0xb3,0xb3,X,Y,0x5b])
            uart.write(data)    #打印XY轴的偏移坐标
            
     
            print("X轴偏移坐标 : ",X)
            print("Y轴偏移坐标 : ",Y)
    
    
            print("帧率 : ",clock.fps())
    
    

    STM32代码部分

    解决的openmv发送的问题,现在就要解决如何能稳定无误的接收到数据,并解析
    我采用了比较通用的(帧头、帧头、数据+、帧尾)当STM32这是一帧数据解析到这样一帧数据时,就得到了我们想要的数据。以下是STM32中的代码
    这个是中断处理的函数

    void USART1_IRQHandler(void)                	//串口1中断服务程序
    	{
    	u8 com_data;
    #if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    	OSIntEnter();    
    #endif
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    		{
    			USART_ClearFlag(USART1,USART_FLAG_RXNE);
          com_data = USART1->DR;
    			Openmv_Receive_Data(com_data);     //openmv数据处理函数
    			Openmv_Data();		                 //openmv数据处理函数		
     		
         } 
    #if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    	OSIntExit();  											 
    #endif
    } 
    

    数据解析部分的程序

    #include "openmv.h"
    int openmv[5];//stm32接收数据数组
    int8_t OpenMV_X;          /*OPENMV X 轴反馈坐标*/
    int8_t OpenMV_Y;          /*OPENMV X 轴反馈坐标*/
    
    
    int i=0;
    
    void Openmv_Receive_Data(int16_t data)//接收Openmv传过来的数据
    {
    	static u8 state = 0;
    	if(state==0&&data==0xb3)
    	{
    		state=1;
    		openmv[0]=data;
    	}
    	else if(state==1&&data==0xb3)
    	{
    		state=2;
    		openmv[1]=data;
    	}
    	else if(state==2)
    	{
    		state=3;
    		openmv[2]=data;
    	}
    	else if(state==3)
    	{
    		state = 4;
    		openmv[3]=data;
    	}
    
    	else if(state==4)		//检测是否接受到结束标志
    	{
            if(data == 0x5B)
            {
                state = 0;
                openmv[4]=data;
                Openmv_Data();
            }
            else if(data != 0x5B)
            {
                state = 0;
                for(i=0;i<5;i++)
                {
                    openmv[i]=0x00;
                }           
            }
    	}    
    	else
    		{
    			state = 0;
                for(i=0;i<5;i++)
                {
                    openmv[i]=0x00;
                }
    		}
    }
    
    void Openmv_Data(void)
    {
        OpenMV_X=openmv[2];
        OpenMV_Y=openmv[3];
    
    }
    
    
    

    需要注意这里的帧头和帧尾需要和OPENMV设置的帧头帧尾匹配。

    主函数部分采用串口直接打印
    经过多次测试把串口打印放在每5ms中断的定时器服务函数中是会出现掉帧的现象,而且数据解析也失效了,这点问题还不知道如何解决。

    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "usart.h"
    #include "holder.h" 
    #include "oled.h"
    #include "mpu6050.h"
    #include "control.h"
    #include "HMI.h"
    #include "usart2.h"	
    #include "openmv.h"
    /*全局变量*/
    
    
    
     int main(void)
     {		
     	u16 t;  
    	u16 len;	
    	u16 times=0;
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    
    // 	
    //	IIC_Init();
    //  delay_ms(500);
    //	MPU6050_initialize();         /*MPU6050初始化*/	
    //  DMP_Init();
    	 
    	LED_Init();			              /*LED端口初始化*/
    	KEY_Init();                   /*初始化与按键连接的硬件接口*/
     
      uart_init(115200);	          /*opemv串口接收*/ 
    	uart3_init(9600);             /*串口屏*/
    	uart2_init(9600);            /*串口发送printf*/
    	 
    	TIM2_Getsample_Int(4999,71);	/*5ms定时中断*/
      PWM_Mode_Config();  	        /* 舵机初始化*/ 
    	OLED_Init();                  /*OLED初始化*/
    	OLED_Clear();                 /*主界面*/ 
     	while(1)
    	{
    		
       	 printf("\n\r X偏移= %.d Y偏移= %.d \n\r",OpenMV_X ,OpenMV_Y);   /*X轴左负右正  */
    
    	}	
    }
    
    
    
    

    运行效果图片

    openmv端数据
    这个是通过串口打印助手所得到的数据
    可以看出两端的数据还是很匹配的。
    调试的时候要把串口助手的波特率调节到与STM32串口的所匹配值
    经过多次测试后可以在STM32端稳定得出所要要数据,而且帧速率也不错

    展开全文
  • STM32串口发彩色图片

    2020-03-20 14:53:52
    正点原子miniSM32采集OV7725图像数据,发送给串口,上位机接收数据,并解析为bmp图片.更多请看博客,【STM32调试(一)】串口发送像素,上位机解析显示。
  • 该程序编写了STM32F103三个串口初始化程序,可快速调用,同时支持接收和发送单个字符和多个字符,并存储数据,方便接收数据后的通信协议校验,支持自定义接收数据最大长度,重写printf函数。
  • STM32F103单片机上用FreeRTOS传输串口数据,在串口中断中用队列存储串口接收的数据,然后再定时器中断中,用队列接收串口数据并打印出来。
  • STM32 A串口2接收STM32 B的数据,同时STM32 A使用串口1发送给电脑
  • STM32H750的IDLE串口空闲中断、DMA传输UART接收数据STM32CUBEMX生成MDK5编译
  • STM32串口通信配置细节

    千次阅读 2021-01-21 23:55:53
    2021-01-12 学习日志STM32f1库函数开发学习实战二 · 串口通信1. 背景知识2. usart文件夹介绍 STM32f1库函数开发学习 实战二 · 串口通信 1. 背景知识 DMA 通信方式 LIN总线 DMA,Direct Memory Access,...




    STM32f1库函数开发学习

    实战二 · 串口通信

    1. 背景知识

    • DMA
    • 通信方式
    • LIN总线

    • DMA,Direct Memory Access,存储器直接访问,一种高速数据传输操作,允许外设与存储器、外设与外设之间直接交换数据。

      CPU 和 DMA 控制器的传输过程处于并行操作状态,大大提高整个计算机系统效率。

      适用于一些高速的I/O设备(kBps),例如磁盘存取、图像处理、高速数据采集、同步通信中的收/发信号。

      DMAC,DMA控制器,负责DMA传送全过程控制的硬件电路。

      DMA

      参考文章:DMA总结


    • 通信方式
      数据传输,按照数据流方向可分为三种传输方式:

      • 单工通信
      • 半双工通信
      • 全双工通信

      单工通信只支持单方向传输数据,任何时候不能改变传输方向。为了保证数据不失真,需要校验位,校验出错时通过监控信道发送请求重发信号。

      适用于数据收集系统,例如计算机和打印机,只有计算机向打印机传输数据。

      半双工通信,支持两个方向传输,但同一时刻只能存在一个方向上的传输,是一种可调换方向的单工通信,例如对讲机。

      全双工通信,允许数据同时在两个方向上传输,有两个传输道路,是两个单工通信结合。

      全双工通信效率高,控制简单,造价高,例如手机、计算机。


    • LIN总线

      Local Interconnect Network,本地互联网络总线。

      LIN主要功能是为CAN总线网络提供辅助功能:

      • 单主多从组网方式,最多16节点,1主15从
      • 硬件要求低,只需要UART/SCI接口,几乎所有的MCU都支持LIN
      • 不需要单独的晶振
      • 只需要一根信号线,单总线设备
      • 传输速率20Kbps
      • 新节点不影响原有节点的硬件

      LIN网络节点任务分为主机任务和从机任务,从机任务在主机节点和从机节点都可以运行。

      LIN通信可以用作“主从通信”、“基于时间表的通信”,后期可以做个多机通信处理系统?

      参考文章:





    2. usart文件夹介绍

    usart文件夹包括 .c 文件和 .h 文件,针对串口1进行了初始化和中断接收,用其他串口时需要更改。主要包括两个函数:

    1. uart_init 函数,串口初始化
    2. USART1_IRQHandler 函数 ,中断响应函数


    下面来解剖这两个函数


    1.uart_init 函数

    1. 引入32位参数 波特率(bound)
    void uart_init(u32 bound)
    

    1. 定义结构体
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    

    1. 开启时钟使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA
    |RCC_APB2Periph_AFIO, ENABLE);
    //使能 USART1,GPIOA 时钟
    //以及复用功能时钟
    

    使用一个内置外设的时候,要首先使能相应的GPIO时钟,然后使能复用功能时钟内置外设时钟

    不知道内置外设应该开启哪个时钟使能的时候,在参考手册搜索“系统架构/系统结构”:
    系统结构图


    1. 初始化GPIO端口为特定状态
    //USART1_TX PA.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 PA.10 
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
     //PA.10
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
     //浮空输入
     GPIO_Init(GPIOA, &GPIO_InitStructure); 
     //初始化 GPIOA.10 接收端
    

    GPIO配置步骤:

    • 时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    • 引脚号 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;
    • 端口翻转速度 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    • 引脚模式 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    • 端口初始化 GPIO_Init(GPIOA, &GPIO_InitStructure);

    如何查找GPIO端口应该配置为什么模式


    在《STM32中文参考手册》中搜索“外设的GPIO配置”,得到以下几个表格(以下不全):
    GPIO配置1
    GPIO配置2GPIO配置3


    1. Usart_1 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); 
    //中断优先级配置
    

    NVIC_IRQChannel:

    定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。

    例如 USART1_IRQn。


    1. 串口1初始化参数以及使能
    //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); 
     //初始化串口
    
     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
     //开启中断
     USART_Cmd(USART1, ENABLE); 
     //使能串口
    
    



    串口配置的一般步骤

    1. 串口时钟使能,GPIO时钟使能
    2. 串口复位
    3. GPIO端口模式设置
    4. 串口参数初始化
    5. 开启中断,初始化NVIC
    6. 使能串口
    7. 中断处理函数

    几个配置需要的库函数:

    1. 串口时钟使能

    查询系统架构图可知,USART1挂在APB2下,需要开启时钟:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);

    2. 串口复位
    复位用于设备异常时重新配置,系统刚开始工作时也需要复位。
    USART_DeInit(USART1); //复位串口 1


    3. 串口参数初始化

    需要初始化的参数是:波特率、字长、停止位、奇偶校验位、硬件数据流控制、收发模式

    通过初始化函数完成:
    USART_Init(USART1, &USART_InitStructure);
    结构体的成员变量配置示例如下

    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); 
    //初始化串口
    

    4. 数据发送与接收

    STM32的发送接收通过寄存器USART_DR实现,这是一个双寄存器,包含了TDR和RDR,写数据时串口就自动发送,收到数据时存储在内

    发数据:
    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

    读数据:
    uint16_t USART_ReceiveData(USART_TypeDef* USARTx);


    5. 串口状态

    通过寄存器USART_SR读取串口状态,一共有32位只取前10位,一般我们只关注第5、6位RXNE和TC:

    USART_SR
    RXNE(读数据寄存器非空):该位为1的时候,说明有数据被接收到,并且可读。此时需要尽快读取USART_DR,然后将RXNE清零或者直接置0清除

    TC(发送完成):该位被置位时,USART_DR数据已经发送完成,可以设置中断。也有两种清零方式:读USART_SR,写USART_DR;直接将TC写0

    读取串口状态的库函数:

    USART_GetFlagStatus(USART1, USART_FLAG_RXNE);

    USART_GetFlagStatus(USART1, USART_FLAG_TC);

    串口的状态是通过宏定义实现的:

    #define USART_IT_PE ((uint16_t)0x0028)
    #define USART_IT_TXE ((uint16_t)0x0727)
    #define USART_IT_TC ((uint16_t)0x0626)
    #define USART_IT_RXNE ((uint16_t)0x0525)
    #define USART_IT_IDLE ((uint16_t)0x0424)
    #define USART_IT_LBD ((uint16_t)0x0846)
    #define USART_IT_CTS ((uint16_t)0x096A)
    #define USART_IT_ERR ((uint16_t)0x0060)
    #define USART_IT_ORE ((uint16_t)0x0360)
    #define USART_IT_NE ((uint16_t)0x0260)
    #define USART_IT_FE ((uint16_t)0x0160)
    

    6. 串口使能

    通过函数USART_Cmd()实现:

    USART_Cmd(USART1, ENABLE); 
    //使能串口
    

    7. 开启串口响应中断

    当我们需要开启串口中断的时候,需要使能,例如:

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    //开启中断,接收到数据中断
    

    此函数的第二个入口参数是使能串口的类型,例如此例中我们需要在接收到数据的时候产生中断,就需要开启RXNE的中断USART_IT_RXNE

    如果需要在发送数据结束的时候产生中断,则:

    USART_ITConfig(USART1,USART_IT_TC,ENABLE);
    //数据发送结束产生串口中断
    

    8. 获取中断状态

    比如我们使能了某个串口发生中断,当中断发生了,可以调用函数判断是否完成中断:

    USART_GetITStatus(USART1, USART_IT_TC)
    

    返回值是SET,则串口发送完成中断






    2. USART1_IRQHandler

    USART1_IRQHandler函数是串口1的中断响应函数,串口1发生中断时会跳转到其中去执行

    1. if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)

    用上面列举的常用库函数中第八句(获取中断状态),判断是否接受中断,如果接收了中断则读取串口接收到的数据:

    1. Res = USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据

    数据赋给变量Res。读到数据之后就对数据进行分析。

    这里有一个接收协议,利用数组USART_RX_BUF[],接收状态“寄存器”USART_RX_STA (实为一个全局变量,但是起到类似寄存器的作用),实现对串口数据的接收管理。

    • USART_RX_BUF[] 的长度由USART_REC_LEN定义:
      u8 USART_RX_BUF[USART_REC_LEN];

      USART_REC_LEN是位于 usart.h中定义的一个全局参数,定义最大接收的字节数

    • USART_RX_STA 是一个接收状态寄存器,定义表如下:

    USART_SR
    当接收到数据时,把数据保存在USART_RX_BUF[]中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数。

    当接收到回车( 回车由0X0D和0X0A组成 ) 的第一个字节0X0D (0x0D,asc码是13,指的是回车\r,把光标置于本行行首) 时,停止计数;

    等待0X0A (0x0A,asc码是10,指的是换行 \n,把光标置于下一行的同一列) ,标记USART_RX_STA的第15位接收完成标志,完成一次接收,等待第15位被清除后完成一次接收。

    如果0X0D回车来迟,而数据超过USART_REC_LEN时,会丢弃前面的数据重新接收


    配置示例

    void USART1_IRQHandler(void) //串口 1 中断服务程序
    {
    	u8 Res;
    	#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 为真,则需要支持 OS
    	OSIntEnter(); 
    	#endif
    
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
    	//接收中断(接收到的数据必须是 0x0d 0x0a 结尾) 		
    	{
    		Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
    		if((USART_RX_STA&0x8000)==0)//接收未完成
    		{
    			if(USART_RX_STA&0x4000)//接收到了 0x0d
    			{
    				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
    				else USART_RX_STA|=0x8000; //接收完成了
    			}
    		else //还没收到 0X0D
    			{
    				if(Res==0x0d)USART_RX_STA|=0x4000;
    				else
    				{
    					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
    					USART_RX_STA++;
    					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
    					//接收数据错误,重新开始接收 
    				} 
    			}
    		} 
    	} 
    	
    #if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 为真,则需要支持 OS
    OSIntExit(); 
    #endif
    }
    
    
    
    
    

    关于OS操作系统的研究日后再进行






    3. 硬件电路

    查看原理图:
    U1
    USART1的RXD和TXD位于PA10和PA9,再次查找得到电气连接方式:

    电气连接

    这里发现串口1的TXD和RXD需要用跳线帽跟PA9、PA10连接在一起






    4. 主函数的一些说明

    usart.h文件中可以引入"stdio.h"头文件,并且加入一段代码,即可提供printf()函数支持,直接利用printf函数向串口发送我们需要的内容。原理及操作见下文:

    STM32使用printf打印串口

    我们来看一下主函数设计的几个示例及要点:

    1. 接收数据部分

    if(USART_RX_STA & 0x8000)
    { 
    	len = USART_RX_STA&0x3fff;//得到此次接收到的数据长度
    	printf("\r\n 您发送的消息为:\r\n");
    	for(t=0;t<len;t++){
    		USART1->DR = USART_RX_BUF[t];
    		while((USART1->SR & 0X40) == 0);}//等待发送结束
    	printf("\r\n\r\n");//插入换行
    	USART_RX_STA = 0;
    }
    

    USART_RX_STA的bit15表示接收完成标志,bit14表示接收到0X0D


    USART_RX_STA&0x8000,即bit15位比较,若为1则接受完成,之后再接收判断长度。


    判断长度就是剩余14位比较,USART_RX_STA&0x3fff,0x3fff即0011 1111 1111 1111,bit相同则为1否则为0,便可得到USART_RX_STA的低14位的值,便得到其长度


    2. 发送数据部分

    else{
    		times ++;
    		if(times % 5000 == 0){
    			printf("\r\n123456789\r\n");
    			printf("asdfghjkl\r\n\r\n\r\n");}
    		if(times % 200 == 0)printf("hello world\r\n");  
    		if(times % 30 == 0)LED0 =! LED0;
    		//LED闪烁指示系统还在运行
    		delay_ms(10);   
    	}
    
    展开全文
  • 这里写目录标题串口发送数据串口接受数据 串口发送数据 1、串口发送数据最直接的方式就是标准调用库函数 。 void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 第一个参数是发送的串口号,第二个参数是...

    串口发送数据

    1、串口发送数据最直接的方式就是标准调用库函数 。

    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
    

    第一个参数是发送的串口号,第二个参数是要发送的数据了。但是用过的朋友应该觉得不好用,一次只能发送单个字符,所以我们有必要根据这个函数加以扩展:

    void Send_data(u8 *s)
    {
     while(*s!='\0')
     { 
      while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET); 
      USART_SendData(USART1,*s);
      s++;
     }
    }
    

    以上程序的形参就是我们调用该函数时要发送的字符串,这里通过循环调用USART_SendData来一 一发送我们的字符串。

    while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET);
    

    这句话有必要加,他是用于检查串口是否发送完成的标志,如果不加这句话会发生数据丢失的情况。这个函数只能用于串口1发送。有些时候根据需要,要用到多个串口发送那么就还需要改进这个程序。如下:

    void Send_data(USART_TypeDef * USARTx,u8 *s)
    {
     while(*s!='\0')
     { 
      while(USART_GetFlagStatus(USARTx,USART_FLAG_TC )==RESET); 
      USART_SendData(USARTx,*s);
      s++;
     }
    }
    

    这样就可实现任意的串口发送。但有一点,我在使用实时操作系统的时候(如UCOS,Freertos等),需考虑函数重入的问题。
    当然也可以简单的实现把该函数复制一下,然后修改串口号也可以避免该问题。然而这个函数不能像printf那样传递多个参数,所以还可以在改进,最终程序如下:

    void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
    {
     const char *s;
     int d;   
     char buf[16];
     
     va_list ap;
     va_start(ap, Data);
     
     while ( * Data != 0 )     // 判断是否到达字符串结束符
     {                              
      if ( * Data == 0x5c )  //'\'
      {           
       switch ( *++Data )
       {
        case 'r':                 //回车符
        USART_SendData(USARTx, 0x0d);
        Data ++;
        break;
     
        case 'n':                 //换行符
        USART_SendData(USARTx, 0x0a); 
        Data ++;
        break;
     
        default:
        Data ++;
        break;
       }    
      }
      
      else if ( * Data == '%')
      {           //
       switch ( *++Data )
       {    
        case 's':            //字符串
        s = va_arg(ap, const char *);
        
        for ( ; *s; s++) 
        {
         USART_SendData(USARTx,*s);
         while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
        }
        
        Data++;
        
        break;
     
        case 'd':   
         //十进制
        d = va_arg(ap, int);
        
        itoa(d, buf, 10);
        
        for (s = buf; *s; s++) 
        {
         USART_SendData(USARTx,*s);
         while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
        }
        
        Data++;
        
        break;
        
        default:
        Data++;
        
        break;
        
       }   
      }
      
      else USART_SendData(USARTx, *Data++);
      
      while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );
      
     }
    }
    

    该函数就可以像printf使用可变参数,方便很多。通过观察函数但这个函数只支持了%d,%s的参数,想要支持更多,可以仿照printf的函数写法加以补充。
    2、 直接使用printf函数。
    很多朋友都知道想要STM32要直接使用printf不行的。需要加上以下的重映射函数;
    在这里插入图片描述如果不想添加以上代码,也可以勾选以下的Use MicroLI选项来支持printf函数使用:
    在这里插入图片描述

    串口接受数据

    串口接收最后应有一定的协议,如发送一帧数据应该有头标志或尾标志,也可两个标志都有。
    这样在处理数据时既能能保证数据的正确接收,也有利于接收完后我们处理数据。串口的配置在这里就不在赘述,这里我以串口2接收中断服务程序函数且接收的数据包含头尾标识为例。

    #define Max_BUFF_Len 18
    unsigned char Uart2_Buffer[Max_BUFF_Len];
    unsigned int Uart2_Rx=0;
    void USART2_IRQHandler() 
    {
     if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生 
     {
      USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
        
      Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);     //接收串口1数据到buff缓冲区
      Uart2_Rx++; 
            
      if(Uart2_Buffer[Uart2_Rx-1] == 0x0a || Uart2_Rx == Max_BUFF_Len)    //如果接收到尾标识是换行符(或者等于最大接受数就清空重新接收)
      {
       if(Uart2_Buffer[0] == '+')                      //检测到头标识是我们需要的 
       {
        printf("%s\r\n",Uart2_Buffer);        //这里我做打印数据处理
        Uart2_Rx=0;                                   
       } 
       else
       {
        Uart2_Rx=0;                                   //不是我们需要的数据或者达到最大接收数则开始重新接收
       }
      }
     }
    }
    

    数据的头标识为“\n”既换行符,尾标识为“+”。该函数将串口接收的数据存放在USART_Buffer数组中,然后先判断当前字符是不是尾标识,如果是说明接收完毕,然后再来判断头标识是不是“+”号,如果还是那么就是我们想要的数据,接下来就可以进行相应数据的处理了。但如果不是那么就让Usart2_Rx=0重新接收数据。
    这样做的有以下好处:
    1.可以接受不定长度的数据,最大接收长度可以通过Max_BUFF_Len来更改
    2.可以接受指定的数据
    3.防止接收的数据使数组越界
    这里我的把接受正确数据直接打印出来,也可以通过设置标识位,然后在主函数里面轮询再操作。

    以上的接收形式,是中断一次就接收一个字符,这在UCOS等实时内核系统中频繁的中断,非常消耗CPU资源,在有些时候我们需要接收大量数据时且波特率很高的情况下,长时间中断会带来一些额外的问题。
    所以以DMA形式配合串口的IDLE(空闲中断)来接受数据将会大大的提高CPU的利用率,减少系统资源的消耗。首先还是先看代码。

    #define DMA_USART1_RECEIVE_LEN 18
    void USART1_IRQHandler(void)                                 
    {     
        u32 temp = 0;  
        uint16_t i = 0;  
          
        if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  
        {  
            USART1->SR;  
            USART1->DR; //这里我们通过先读SR(状态寄存器)和DR(数据寄存器)来清USART_IT_IDLE标志    
            DMA_Cmd(DMA1_Channel5,DISABLE);  
            temp = DMA_USART1_RECEIVE_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); //接收的字符串长度=设置的接收长度-剩余DMA缓存大小 
            for (i = 0;i < temp;i++)  
            {  
                Uart2_Buffer[i] = USART1_RECEIVE_DMABuffer[i];  
                    
            }  
            //设置传输数据长度  
            DMA_SetCurrDataCounter(DMA1_Channel5,DMA_USART1_RECEIVE_LEN);  
            //打开DMA  
            DMA_Cmd(DMA1_Channel5,ENABLE);  
        }        
    } 
    

    之前的串口中断是一个一个字符的接收,现在改为串口空闲中断,就是一帧数据过来才中断进入一次。而且接收的数据时候是DMA来搬运到我们指定的缓冲区(也就是程序中的USART1_RECEIVE_DMABuffer数组),是不占用CPU时间资源的。

    最后在讲下DMA的发送:

    #define DMA_USART1_SEND_LEN 64
    void DMA_SEND_EN(void)
    {
     DMA_Cmd(DMA1_Channel4, DISABLE);      
     DMA_SetCurrDataCounter(DMA1_Channel4,DMA_USART1_SEND_LEN);   
     DMA_Cmd(DMA1_Channel4, ENABLE);
    }
    

    这里需要注意下DMA_Cmd(DMA1_Channel4,DISABLE)函数需要在设置传输大小之前调用一下,否则不会重新启动DMA发送。

    展开全文
  • STM32工程中可直接调用USART1-UART5,封装好串口中断,接收指针,数据传输稳定可靠无误。
  • STM32串口通信例程,采用CubeMx进行配置,方便实用,是很不错的STM32通信例程。例程中使用了回调函数,可以让读者体会到回调函数的重要性
  • stm32串口1串口2 DMA方式收发数据

    热门讨论 2014-08-15 22:58:24
    stm32串口1串口2,DMA方式收发数据。使用定时器定时查询DMA接收到的数据,当串口数据空闲中断,将数据拷贝到缓冲区,交由其他程序处理。可以接收任意大小的数据包。本方法占用CPU时间极少,尤其是波特率很高时,...
  • STM32串口自定义数据接收协议

    千次阅读 2021-09-10 16:20:16
    最近使用STM32串口数据收发,遇到了一些问题。折腾了一番,在此记录一下。

    写在前面

    最近使用STM32做串口数据收发,遇到了一些问题。折腾了一番,在此记录一下。

    0 需求

    1. 云平台通过“发布消息”,下行指令。
    2. 4G模块接收平台下行指令并转发到单片机,单片机通过串口(UART3)做数据接收与分析。
      总的来说,比较简单。单片机和4G通过串口通信,当4G与平台连接之后,在保证数据在平台与4G模块之间能正常流转的情况下,可视为单片机使用串口直接与平台进行通信。而原子亦给出了串口通信的相关例程,其中包含串口收发实验,可做参考。

    1 问题产生

    为了实现需求,先进行两个小实验
    3. 模块+上位机实验 : 验证4G模块与平台之间数据收发正常。
    4. 电路板串口数据接收实验 :排除电路板硬件异常问题。

    1.1 模块+上位机实验

    平台发布消息,模块TX引脚输出。通过CH340与串口调试助手相连,接收并显示数据。验证模块能否正常收发数据。
    在这里插入图片描述
    ---------------------补一张串口接收数据图--------------

    可以正常收发数据,排除模块问题。
    ASCII编码对照表_911查询

    1.2 电路板串口数据接收实验

    实验硬件:UART1(COM3) , UART3(COM1)实验。
    实验现象:UART1 接收到的消息通过UART1打印到串口;UART3 接收到的消息亦通过UART1打印到串口。(UART1做串口调试使用)
    实验结论: 排除电路板硬件异常原因,UART1 & UART3 可以正常收发数据。
    示例代码:

    while(1)
    {
    		
       if(USART_RX_STA&0x8000)
    	{		
    		LED0 = 0;			 //点亮LED显示接收到消息	
    		len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
    		printf("\r\nUART1发送的消息为:\r\n");
    		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);	//发送接收到的数据
    		while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
    		printf("\r\n\r\n");//插入换行
    		USART_RX_STA=0;
    	}
    	else if(USART3_RX_STA&0x8000)
    	{
    		LED0 = 1;			 //熄灭LED显示接收到消息	
    		len=USART3_RX_STA&0x3fff;//得到此次接收到的数据长度
    		printf("\r\nUART3发送的消息为:\r\n");
    		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART3_RX_BUF,len,1000);	//发送接收到的数据
    		while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
    		printf("\r\n\r\n");//插入换行
    		USART3_RX_STA=0;
    	} 
    }	
    

    在这里插入图片描述

    1.3 问题来了!

    当以上两个小测试完成时,可以确定整个系统软件与硬件无误。也就是说,当电路板上插上4G模块时,即可实现单片机与平台通信的功能!
    然而,当平台发布数据时,单片机未能接收到数据。通过软件调试等操作发现,UART3并未进入到接收到数据中断。用示波器测量模块TX引脚,平台发布消息时确实有信号输出!迷惑行为,,,,,,

    2 开始分析

    2.1 串口数据格式

    如下图,可以看出

    1. 无数据时,电平始终为‘1’
    2. 起始位为1位‘0’,停止位为1位‘1’
      在这里插入图片描述
      串口助手配置为:
      bound :115200 ;停止位 :1 ;数据位 :8 ; 检验位 : None
      在这里插入图片描述

    2.2 测一波波形

    以上两个小测试确实可以验证软件与硬件无误。那么为什么单片机可以接收上位机传来的数据,而不能接收4G模块转发来的数据呢?二者数据有何区别?
    验证方法 : 上位机和平台同时发送信息,测其输出信号。测试数据 11(0011_0001 0011_0001)

    • 注 :串口助手及平台发布的数据为字符(ASCII),助手可选择hex发送及显示。
      1. 平台发布数据 11(0011_0001 0011_0001) ,4G转发数据波形如图
      在这里插入图片描述
      **波形分析:**非常漂亮的波形,可以读出数据为 0011_0001 0011_0001(起始位为1位‘0’,停止位为1位‘1’)
      2. 串口助手通过CH340发送数据 11(0011_0001 0011_0001)
      在这里插入图片描述
      CH340 的 TX 引脚输出波形如下:X10_1000_1100_10_1000_1100_10_1011_0000_10_0101_0000_1X
      在这里插入图片描述
      参考串口数据格式,可知:串口发送的数据为:0011_0001 , 0011_0001 , 0000_1101 , 0000_1010

    **波形分析:**前两个数据值为32,对应的字符为 ‘1’,‘1’,与发送数据一致。多了后两个值13 10,查询ASCII码为 0D 0A。对应换行键和归位键,嗯?
    在这里插入图片描述
    查看串口助手,果然!默认勾选了“发送新行”
    在这里插入图片描述
    再次测试,勾选了“发送新行”数据可以被接收;不勾选了“发送新行”,数据不被接收!可以看出“发送新行”即为单片机识别数据的“校验”格式。那么程序中一定有与“校验”相关的代码,那就找到他!

    • 测波形时遇到一个现象,在这记录一下。

    当直接接CH340输出时,图形在上。可以看到高电平只有1,5V左右,电压驱动并不强;当CH340与单片机相连时,发送波形测得的波形如下。当时怀疑过是高电平的问题,在单片机上接了个上拉电阻,波形是好看了,但依旧没有解决问题。因为串口低电平有效,并不要求严格的高电平。
    在这里插入图片描述

    3 代码分析

    关于串口接收的代码如下,一眼就可看到关于0x0a,0x0d的判断,确认是结尾校验无误了。若要修改为 ‘**’ 校验,改为0x2a,0x2a即可。修改后测试成功,没图。

    if(huart->Instance==USART3)//如果是串口1
    {
    	if((USART3_RX_STA&0x8000)==0)//接收未完成
    	{
    		if(USART3_RX_STA&0x4000)//接收到了0x0d
    		{
    			if(aRx3Buffer[0]!=0x0a)USART3_RX_STA=0;//接收错误,重新开始
    			else USART3_RX_STA|=0x8000;	//接收完成了 
    		}
    		else //还没收到0X0D
    		{	
    			if(aRx3Buffer[0]==0x0d)USART3_RX_STA|=0x4000;
    			else
    			{
    				USART3_RX_BUF[USART3_RX_STA&0X3FFF]=aRx3Buffer[0] ;
    				USART3_RX_STA++;
    				if(USART3_RX_STA>(USART_REC_LEN-1))USART3_RX_STA=0;//接收数据错误,重新开始接收	  
    			}		 
    		}
    	}
    }
    
    • 注 :关于0x0a,0x0d 网上也有不少资料参考。原子的串口助手也有提示正确格式。由于很少使用16进制发送,一致没有注意到!
      在这里插入图片描述

    4 新的问题:串口数据累加

    新的问题收测试时出现寄存器数据累加情况,具体表现为:

    1. 当串口UART3接收到的数据未加结束校验“**”,单片机未能判断数据接收完毕。
    2. 当下次数据来临(带校验),单片机判断数据发送完毕。通过串口1将数据输出。
      在这里插入图片描述
      再使用例程时,取消"发送新行",会出现同样的问题,由此可以判断是底层代码问题。
      在这里插入图片描述
      猜测:串口在接收未加校验的数据时,已经将数据存入串口接收缓冲buff中。当之后数据(带校验)来临时,继续将数据存入buff,并判断数据接收完毕。此时,多次发送的数据集中在同一buff中,数据为及时清空,由此导致数据累加的情况。
      再来分析这段代码:
      其中,USART3_RX_STA 是串口接收状态标记。定义为: u16 USART_RX_STA=0; 功能如下
      | bit15 | bit14 | bit13~0 |
      | ---------------- | -------------- | -------------------- |
      | 接收完成标志0x0a | 接收到0X0d标志 | 接收到的有效数据个数 |
    if(huart->Instance==USART3)	//如果是串口3
    {		
    	if((USART3_RX_STA&0x8000)==0)		//接收未完成  USART3_RX_STA最高位判断
    	{
    		if(USART3_RX_STA&0x4000)		//接收到了第一个0x2a  USART3_RX_STA次高位判断
    		{
    			if(aRx3Buffer[0]!=0x0a)
    			{
    				USART3_RX_STA=0;		//接收错误,重新开始
    			}
    			else USART3_RX_STA|=0x8000;	//接收完成了 
    		}
    		else 							//还没接收到第一个0x2a
    		{	
    			if(aRx3Buffer[0]==0x0d)
    			{
    				USART3_RX_STA|=0x4000;
    			}
    			else
    			{
    				USART3_RX_BUF[USART3_RX_STA&0X3FFF]=aRx3Buffer[0] ;  //0X3FFF  USART3_RX_STA 低14位是数据
    				USART3_RX_STA++;
    				if(USART3_RX_STA>(USART_REC_LEN-1))
    				{
    					USART3_RX_STA=0;//接收数据错误,重新开始接收
    				}	  
    			}		 
    		}
    	}
    }
    

    大致画了下流程图,时间关系。不再文字分析了,几个判断嵌套。串口通信实验讲解里关于USART_RX_STA的问题与思考这篇博客文字分析比较详细,推荐一波。
    在这里插入图片描述
    可以看出,与猜测一致。串口接收缓冲buff并没有进行数据清除。当数据(带校验)未临时,之前的数据会一直寄存在buff中,直到最终发送完毕。(当然数据累加有一定上限,USART_REC_LEN)

    若要消除数据累加的情况,就必须在接收完一次不带校验的数据后,及时清除缓冲buff。实际使用中,两次接收数据之间有一定间隔,若能在间隔之中清除buff,即可规避。故加入以下代码,测试一下。

    	/* 间隔一定时间清空串口缓冲BUF USART3_RX_BUF */
    		if (time_10_ms)		 //定时器 10ms
    		{ 
    			time_10_ms = 0 ;
    			memset(USART3_RX_BUF, 0, sizeof USART3_RX_BUF);  
    		}
    

    在这里插入图片描述
    测试结果:不加校验的数据串口不识别,定时清空处理,无数据累加情况。测试ok!

    总结

    没啥写的,强迫症凑一凑。

    展开全文
  • //设置传输数据长度 DMA_SetCurrDataCounter(DMA1_Channel6,40); //打开DMA DMA_Cmd(DMA1_Channel6,ENABLE); } __nop(); } ![图片说明]...
  • 一.关于如何配置通过中断的...8.编写收发数据处理函数 二.初始化串口函数 1.使能串口时钟、使能GPIO时钟 //使能GPIOA RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能串口1 RCC_APB2Periph
  • STM32串口发送数据

    万次阅读 2019-05-15 15:35:53
    串口通信经常作为开发调试的工具,所以先介绍下串口通信。 串口通讯(Serial Communication)...目前STM32一般只使用 RXD、TXD 以及 GND 三条信号线,直接传输数据信号。 STM32串口通信外设有USART和UART。USART是...
  • STM32串口问题

    2017-11-08 17:08:11
    stm32给pc端发送数据,可以用串口调试助手查看,级可以接受又可以发送,接受的可以解决。但是发送的话目前还没有解决
  • STM32如何利用串口发送接收数据

    千次阅读 热门讨论 2021-05-08 11:38:49
    STM32如何利用串口发送接收数据? 我现在计划利用STM32F103X的串口对迪文屏发送及接收数据。 手中硬件:正点原子开发板(旗舰版),迪文屏(4.3寸),电脑 软件:MCU程序下载:FLYMCU;串口助手:XCOM;迪文屏配置:...
  • stm32串口接收数据的几种方式

    千次阅读 2021-01-26 11:12:09
    stm32串口接收数据的几种方式 转载自:http://bbs.elecfans.com/jishu_357017_1_1.html 本例程通过PC机的串口调试助手将数据发送至STM32,接收数据后将所接收的数据又发送至PC机,具体下面详谈。。。 实例一...
  • STM32 串口 发送 必须 先检测 状态,否则 第一个 字节 无法 发出,发送完毕,必须检测发送状态是否完成,否则,发送不成功,使用stm32f10x调试串口通讯时,发现一个出错的现象,硬件复位重启之后,发送测试数据0x01 ...
  • STM32串口发送数据详解

    万次阅读 2016-06-16 14:04:47
    串口的中断方式: /** * @brief Enables or disables the specified USART interrupts. * @param USARTx: where x can be 1 or 2 to select the USART peripheral. * @param USART_IT: specifies the USART ...
  • STM32F4串口发送

    2018-05-16 14:51:09
    基于STM32F407的UART串口数据的发送,用于传输六自由度机械臂的控制指令。
  • STM32-串口发送数据-过程与配置

    千次阅读 2021-09-21 09:53:05
    stm32f429xx.h F429芯片 stm32f767xx.h F767芯片 stm32f103xx.h F103芯片 stm32fnnnx.x.h 其他芯片 可以在其中找到USART_TypeDef:最终会映射到寄存器的地址。 typedef struct { __IO uint32_t CR1; /*!< USART ...
  • STM32串口DMA发送数据

    2021-10-30 10:16:07
    一、DMA简介 二、实验流程 了解了DMA之后,我们做一个实验:STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据 ...传输数据长度为8 Bit。奇偶检验无,停止位1 接收和发送都使能。 NVI
  • 使用stm32f205调试串口通讯时,发现一个问题,上电后串口发送字符串的第一个字节会丢失,发送测试数据hello,接收端收到的数据为:ello,第一个字符丢失。用示波器抓取波形,发现第一个数据的波型也是丢失的。
  • STM32串口多机通信

    2020-10-17 23:44:53
    STM32 的UART数据寄存器是9位,数据传输先传低位(LSB)--实际应用发现9位数据大时候有丢包错...利用USART可以进行多机处理器通信,其原理就是使从机处于静默模式,由主机在需要的时候发送指令唤醒从机,并传输数据STM
  • 如果串口接收数据数据传输的比较快,这个时候还要使用同一个串口发送数据。 如果是在主函数中使用HAL_UART_Receive();,这个时候不管使用HAL_UART_Transmit();还是使用HAL_UART_Transmit_DMA();发送的数据都不完整...
  • MODBUS_STM32串口通信

    2018-07-24 17:02:57
    1,实验X00 MODBUS - 串口主机.rar 做MODBUS主机,通过串口1不停的发数据。 2,实验X01 MODBUS - 串口从机.rar 做MODBUS从机,通过串口1不停的接收数据。该代码还支持和XCOM的协议传输,XCOM做主机。 测试需要用三...
  • stm32串口发送数据出现问题

    千次阅读 2015-11-18 09:32:12
    在用stm32向上位机发送串口数据的时候,发现发送的个数和上位机接收到的数据个数不匹配,后来发现是延时的问题,在发送的时候加个延时就可以了!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,863
精华内容 4,345
关键字:

stm32串口传输数据