【串口系列】不定长接收多种方式

2023-05-16

目录

背景

声明

开发环境

正文

一、接收中断 + 空闲中断

二、接收中断 + T35定时器中断

T35定时器

三、空闲中断 + DMA + 循环队列


背景

         在单片机开发过程中,串口通讯是一种非常常用的串行通通讯方式,如调试、协议通信、模组驱动等都有大量的应用,而针对串口接收从技术角度可分定长数据接收及不定长数据接收。针对定长数据接收,可使用特定的起始和结束符以及长度进行帧识别,或其它有效的方式;而针对不定长数据接收为本文讨论的重点,起始不定长数据接收已包含了定长数据,因此掌握不定长数据接收是串口编程的重中之重,本文将使用3种方式由浅入深进行讨论,希望能带来一些收获。

声明

本文叙述的4中方式及代码并非适用于所有内核,仅供思路参考,实际应用中需自行验证可行性。

开发环境

主控

STM32F103VET6

IDE

KEIL

正文

         在单片机接收不定长数据时,重点是如何判断一帧数据接收完成,并能够将这一帧数据的长度和数据缓存下来,那么我们要的功能就实现了。下面通过网络分享的资源及工作时的经验,总结出以下几种思路实现供不同实际应用场合应用。

一、接收中断 + 空闲中断

    STM32单片机串口实现了空闲帧检测功能,配合接收缓冲区非空格中断,当一帧数据接收时,利用接收缓冲区非空中断将一个一个字节的数据存入我们自定义的缓存区,并维护一个变量记录帧长度。当空闲中断发生时,意味着一帧的数据已经结束,通过标志置位即可判断这一帧已经结束。

优点:在自动识别帧数据,无需主程序参与

缺点:1. 针对其它厂家或其它型号单片机,不一定支持空闲中断功能

             2. 在串口速率较高时,会导致频繁进入串口中断(接收中断)

部分程序:

/* 变量定义 */
#define DATA_RECV_MAXSIZE (128)
typedef struct data_recv_st
{
     uint8_t buff[DATA_RECV_MAXSIZE] //uint8_t* buff;
     uint16_t len;
     uint8_t  flag;
}data_recv_t;
data_recv_t data_recv;

uint8_t at_rxbuffer[1];
	
/* 串口初始化 */
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;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart1) != HAL_OK)
	{
	  Error_Handler();
	}
	
	HAL_UART_Receive_IT(&huart1,aRxBuffer1,1);   // 开启接收中断
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 开启空闲中断
}

/* 中断函数处理 */
/**
  * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25.
  */
void USART1_IRQHandler(void)
{
  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE) != RESET ) {
        __HAL_UART_CLEAR_OREFLAG(&huart1);
    }
    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_NE) != RESET ) {
        __HAL_UART_CLEAR_NEFLAG(&huart1);
    }
    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_FE) != RESET) {
        __HAL_UART_CLEAR_FEFLAG(&huart1);
    }
    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_PE) != RESET) {
        __HAL_UART_CLEAR_PEFLAG(&huart1);
    }
    if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
    {
        if(data_recv.len < DATA_RECV_MAXSIZE) {
            data_recv.buff[data_recv.len++] = USART1->RDR;
        } else  {
            USART1->RDR;
        }
    }
    if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) 
    {
	   __HAL_UART_CLEAR_IDLEFLAG(&huart1);
        data_recv.flag = 1;
    }
}


二、接收中断 + T35定时器中断

         我们已经实现了接收中断 + 空闲中断的方式,但是这种方式有一个缺点,有些单片机不支持空闲中断。先回到第一种方式,STM32单片机使用空闲中断检测帧是如何实现的呢?通过判断一个字节时间内是否有接收到数据。那么我们可以人为实现,通过一个基本定时器,当接收到新字节时刷新定时器,如果没有接收到新字节,那么定时器在设定的时间后会出现更新中断,在进入这个中断后我们就可以认为一帧数据已经结束了。那么我们如何实现这个定时器呢?在modbus协议中采用了这种方式,并且有个通用的叫法:T35定时器。

T35定时器

    参考链接:https://www.cnblogs.com/mrsandstorm/p/5701867.html

   波特率:每秒钟通过信道传输的信息量称为位传输速率,也就是每秒钟传送的二进制位数,简称比特率。比特率表示有效数据的传输速率,用b/s 、bit/s、比特/秒,读作:比特每秒。如9600b/s:指总线上每秒可以传输9600个bit;

    若按照 1字节起始位 + 8字节数据位 + 2字节停止位 = 11位,那么在9600bps波特率下,可传输 9600 / 11 = 872.7273 个字节,那么一个1字节传输耗费的时间为 1 / 872.7273 = 0.0011s ,按照3.5个桢长度为超时时间计算,超时时间: 0.0011 * 3.5 = 0.0039 s,那么对于50us定时器来说,需要设置的定时周期为 0.0039s / 50us = 7.8个周期

通用计算公式:

3.5 * (1 / ( Baudrate / 11)  * 100000) / 50

  -> 3.5 * (1100000 / Baudrate) / 50

-> 3.5 * 220000 / Baudrate

     -> 7 * 220000 / (Baudrate * 2)

T35定时器实现

         基本定时器使用50us的时基,按照不同的波特率计算出需要n个50us,那么定时器的超时时间为:n*50 us

static void eMBHwInit(u32 ulBaudRate)

{

    u32 usTimerT35_50us;

    /* If baudrate > 19200 then we should use the fixed timer values

     * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.

     */

    if( ulBaudRate > 19200 )

    {

        usTimerT35_50us = 100;       /* 5000us. */

    }

    else

    {

        /* The timer reload value for a character is given by:

         *

         * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )

         *             = 11 * Ticks_per_1s / Baudrate

         *             = 220000 / Baudrate

         * The reload for t3.5 is 1.5 times this value and similary

         * for t3.5.

         */

        usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );

    }

    xMBPortTimersInit((u16)usTimerT35_50us);

}

优点:在自动识别帧数据,无需主程序参与

缺点:1. 需另外增加一个软件定时器实现T35定时器。

           2. 在串口速率较高时,会导致频繁进入串口中断(接收中断)

程序:

         该方式与第一种实现类似,第一种在空闲中断中处理的内容需转至定时器超时中断中进行并在串口中使能接收中断。

static void xMBPortTimersInit(u16 usTim1Timerout50us)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    u16 PrescalerValue = 0;
    TIM_RCC;
    //定时器时间基配置说明
    //HCLK为72MHz,APB1经过2分频为36MHz
    //TIM4的时钟倍频后为72MHz(硬件自动倍频,达到最大)
    //TIM4的分频系数为3599,时间基频率为72 / (1 + Prescaler) = 20KHz,基准为50us
    //TIM最大计数值为usTim1Timerout50u
    PrescalerValue = (u16) (SystemCoreClock / 20000) - 1; 
    //定时器1初始化
    TIM_TimeBaseStructure.TIM_Period = (uint16_t) usTim1Timerout50us;
    TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM, &TIM_TimeBaseStructure);
    //预装载使能
    TIM_ARRPreloadConfig(TIM, ENABLE);
    
    TIM_ClearITPendingBit(TIM,TIM_IT_Update);
    
    TIM_ITConfig(TIM, TIM_IT_Update, DISABLE);
    TIM_Cmd(TIM,  DISABLE);
}

static void eMBHwStart(void)
{
    TIM_ClearITPendingBit(TIM, TIM_IT_Update);
    TIM_ITConfig(TIM, TIM_IT_Update, DISABLE);
    TIM_SetCounter(TIM,0x0000);
    TIM_Cmd(TIM, DISABLE);
}

void uart_T35Init(u32 ulBaudRate)
{
    eMBHwInit(ulBaudRate);
    eMBHwStart();
}

三、空闲中断 + DMA + 循环队列

         在前面两种中,都有共同的缺点:在速率较高,数据量较大时会频繁进入串口中断,如果在中断中不及时处理会导致ORE错误。那么有没有办法能够解决这个问题呢?答案肯定是有的。目前的问题是接收中断导致的问题,如果不使用接收中断,是不是可以解决问题。在STM32中可以使用DMA解决,DMA实现从外设到内存的传输,这样MCU可以在主程序中处理其它任务,而且不会进入接收中断。但是有个问题,DMA传输大数据量时如果只有一个缓存区,那么必然存在数据的拷贝,因为这个缓存区需要接收新的数据,旧的帧数据必须拷贝到其它数据中保存好。这就会引入另外一个问题:https://blog.csdn.net/qq_20999867/article/details/92961110。

        那么我考虑的是使用一个队列,DMA每次接收完一个缓存区后去获取下一个缓存区的地址循环接收,并且维护一个头尾节点保证设备读取正常。

优点:效率较高,支持高速率传输

缺点:实现复杂

Lib_queue.c

#include "lib_queue.h"

#include <string.h>

/**

 * @description: 创建队列

 * @return  {*} 0: 成功 -1: 失败

 * @param {data_queue_t} *queue: 队列对象

 * @param {void} *buf: 队列缓存数组指针

 * @param {unsigned short} queue_len: 队列长度

 * @param {unsigned short} itemsize: 队列单个元素大小,以字节为单位

 */

int data_queue_init(data_queue_t *queue, data_frame_complete_callback_t callback)

{

    assert_param_queue(queue, return queue_false);

    memset(queue, 0, sizeof(data_queue_t));

    queue->front = queue->rear = 0;

    queue->pop_state = QUEUE_STATE_UNLOCKED;

    queue->push_state = QUEUE_STATE_UNLOCKED;

    queue->len = DATA_QUEUE_LEN;

    queue->complete_callback = callback;

    return queue_true;

}

/**

 * @description: 获取队列已存放个数

 * @return  {*}

 * @param {data_queue_t} *queue

 */

int data_queue_use_block(data_queue_t *queue)

{

    assert_param_queue(queue, return queue_false);

    if (queue->front < queue->rear)

    {

        return (queue->front + queue->len - queue->rear);

    }

    else

    {

        return (queue->front - queue->rear);

    }

}

/**

 * @description: 获取剩余队列数据个数

 * @return  {*}

 * @param {data_queue_t} *queue

 */

int data_queue_remain_block(data_queue_t *queue)

{

    assert_param_queue(queue, return queue_false);

    if (queue->front < queue->rear)

    {

        return (queue->rear - queue->front) - 1;

    }

    else

    {

        return (queue->len - (queue->front - queue->rear)) - 1;

    }

}

/**

 * @description: 获取队列总数据个数

 * @return  {*}

 * @param {data_queue_t} *queue

 */

int data_queue_total_block(data_queue_t *queue)

{

    assert_param_queue(queue, return queue_false);

    return (queue->len - 1);

}

/**

 * @description: 判断数据是否已满

 * @return  满: queue_true 不满:queue_false

 * @param {data_queue_t} *queue

 */

int data_queue_is_full(data_queue_t *queue)

{

    assert_param_queue(queue, return queue_false);

    if (data_queue_use_block(queue) == data_queue_total_block(queue))

    {

        return queue_true;

    }

    return queue_false;

}

/**

 * @description: 判断数据是否已空

 * @return  空: queue_true 非空:queue_false

 * @param {data_queue_t} *queue

 */

int data_queue_is_empty(data_queue_t *queue)

{

    assert_param_queue(queue, return queue_false);

    if (data_queue_use_block(queue) == 0)

    {

        return queue_true;

    }

    return queue_false;

}

/**

 * @description: 获取队列空闲块存储地址,获取存储地址后配合DMA或其他方式使用

 * @return  {*}

 * @param {data_queue_t} *queue

 */

data_packet_t *data_queue_get_idleblock_addr(data_queue_t *queue)

{

    assert_param_queue(queue, return NULL);

    if (queue->push_state == QUEUE_STATE_LOCKED)

    {

        return NULL;

    }

    if (data_queue_is_full(queue) == queue_true)

    {

        return NULL;

    }

    queue->push_state = QUEUE_STATE_LOCKED;

    return (&queue->data_packet[queue->front]);

}

/**

 * @description: 数据接收完成,推入队列,实际数据已存在队列中,但需处理头计数

 * @return  {*}

 * @param {data_queue_t} *queue

 */

int data_queue_push(data_queue_t *queue)

{

    assert_param_queue(queue, return queue_false);

    if (queue->push_state != QUEUE_STATE_LOCKED)

    {

        return queue_false;

    }

    queue->front++;

    queue->front = queue->front % queue->len;

    if (queue->complete_callback != NULL)

    {

        queue->complete_callback();

    }

    queue->push_state = QUEUE_STATE_UNLOCKED;

    return queue_true;

}

/**

 * @description: 获取队列数据,并处理尾计数

 * @return  {*}

 * @param {data_queue_t} *queue

 */

data_packet_t *data_queue_pop(data_queue_t *queue)

{

    data_packet_t *p_data_pack = NULL;

    assert_param_queue(queue, return NULL);

    if (data_queue_is_empty(queue) == queue_true)

    {

        return NULL;

    }

    if (queue->pop_state == QUEUE_STATE_LOCKED)

    {

        return NULL;

    }

    queue->pop_state = QUEUE_STATE_LOCKED;

    p_data_pack = &queue->data_packet[queue->rear];

    queue->rear++;

    queue->rear = queue->rear % queue->len;

    queue->pop_state = QUEUE_STATE_UNLOCKED;

    return p_data_pack;

}

/**

  * @brief  Reports the name of the source file and the source line number

  *         where the assert_param error has occurred.

  * @param  file: pointer to the source file name

  * @param  line: assert_param error line source number

  * @retval None

  */

void assert_failed_queue(unsigned char *file, unsigned int line)

{

#ifdef PRINTE

    printf("Wrong parameters value: file %s on line %d\r\n", file, line);

#endif

}

Lib_queue.h

#ifndef __LIB_QUEUE_H

#define __LIB_QUEUE_H

#define queue_true    (0)

#define queue_false   (-1)

#ifndef NULL

#define NULL   ((void*)0)

#endif

void assert_failed_queue(unsigned char *file, unsigned int line);

#ifndef assert_param_queue

#define assert_param_queue(expr, action)   {if(expr == 0) { assert_failed_queue((unsigned char  *)__FILE__, __LINE__);action; }}

#endif

#define  DATA_PACKET_SIZE          (128)

#define  DATA_QUEUE_LEN            (10)

typedef void (*data_frame_complete_callback_t)(void);

typedef enum data_queue_lockstate_et

{

    QUEUE_STATE_UNLOCKED,

    QUEUE_STATE_LOCKED,

}data_queue_lockstate_t;

typedef struct  data_packet_st

{

    unsigned short len;

    unsigned char data[DATA_PACKET_SIZE + 4];

}data_packet_t;

typedef struct  data_queue_st

{

    data_packet_t data_packet[DATA_QUEUE_LEN];

    unsigned short front;           // 数据头,指向下一个空闲存放地址

    unsigned short rear;            // 数据尾,指向第一个数据

    unsigned short len;

    data_queue_lockstate_t  push_state;

    data_queue_lockstate_t  pop_state;

    data_frame_complete_callback_t complete_callback;

}data_queue_t;

int data_queue_init(data_queue_t *queue, data_frame_complete_callback_t callback);

int data_queue_push(data_queue_t *queue);

data_packet_t *data_queue_get_idleblock_addr(data_queue_t *queue);

data_packet_t * data_queue_pop(data_queue_t *queue);

int data_queue_use_block(data_queue_t *queue);

int data_queue_remain_block(data_queue_t *queue);

int data_queue_total_block(data_queue_t *queue);



#endif

串口初始化

data_queue_t data_queue_recive;

data_packet_t *g_cur_data_packet = NULL;



void MX_USART1_UART_Init(void)

{

    /* USER CODE BEGIN USART1_Init 0 */

    /* USER CODE END USART1_Init 0 */

    /* USER CODE BEGIN USART1_Init 1 */

    /* USER CODE END USART1_Init 1 */

    huart1.Instance = USART1;

    huart1.Init.BaudRate = 2000000;

    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;

    huart1.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&huart1) != HAL_OK)

    {

        Error_Handler();

    }

    /* USER CODE BEGIN USART1_Init 2 */

    extern void frame_complete_callback(void);

    data_queue_init(&data_queue_recive, frame_complete_callback);

    g_cur_data_packet = data_queue_get_idleblock_addr(&data_queue_recive);

    HAL_UART_Receive_DMA(&huart1, g_cur_data_packet->data, DATA_PACKET_SIZE);

    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //open idle interrupt

    /* USER CODE END USART1_Init 2 */

}

中断函数处理

/**

  * @brief This function handles USART1 global interrupt.

  */

void USART1_IRQHandler(void)

{

    /* USER CODE BEGIN USART1_IRQn 0 */

    /* USER CODE END USART1_IRQn 0 */

    HAL_UART_IRQHandler(&huart1);

    /* USER CODE BEGIN USART1_IRQn 1 */

    if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)

    {

        __HAL_UART_CLEAR_IDLEFLAG(&huart1);

        HAL_UART_DMAStop(&huart1);

        g_cur_data_packet->len = DATA_PACKET_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

        DMA1_Channel5->CNDTR = DATA_PACKET_SIZE; //set DMA recive byte

        data_queue_push(&data_queue_recive);

        g_cur_data_packet = data_queue_get_idleblock_addr(&data_queue_recive);

        if (g_cur_data_packet == NULL)

        {

            data_queue_pop(&data_queue_recive);

            g_cur_data_packet = data_queue_get_idleblock_addr(&data_queue_recive);

        }

        

        HAL_UART_Receive_DMA(&huart1, g_cur_data_packet->data, DATA_PACKET_SIZE); //enable DMA

    }

    /* USER CODE END USART1_IRQn 1 */

}


版权声明:本文为CSDN博主「Hi,Mr.Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34672688/article/details/115473035

分享不易,点个赞再走吧☺☺☺


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【串口系列】不定长接收多种方式 的相关文章

  • 关于精度、分辨率、LSB的理解

    1 精度 是用来描述物理量的准确程度的 精度有关的有两个很重要的指标 xff1a DNL INL DNL xff1a Differencial NonLiner 微分非线性度INL xff1a Interger NonLiner 积分非线性
  • MPU6050教程(转载)

    如果你想玩四轴 xff0c 想搞什么空中鼠标 xff0c 平衡车等待 xff0c 那么MPU6050真的是太强大了 xff0c 能做很多东西 但是论坛上MPU6050整个教学过程基本上是没有的 xff0c 资源共享 xff0c 不应该只在自
  • Swift 中使用SDWebImage

    SDWebImage是开发中常用的插件 xff0c 主要用于加载外部图片 xff0c 具有缓存图片资源的功能 xff0c 然而SDWebImage是由Object C所写 步骤 xff1a 下载SDWebImage https github
  • VSCode的多个(C/C++)源文件的编译运行(Linux)

    引言 VSCode作为微软推出的一款免费的轻量级的集成开发环境 xff0c 可谓是良心之作 它拥有VS的高颜值 xff0c 而且像瑞士军刀一样 xff0c 拥有丰富的插件 xff0c c c 43 43 java python js htm
  • Android 任务栈简介

    Android 任务栈简介 一个Android应用程序功能通常会被拆分成多喝Activity xff0c 而各个Activity之间通过Intent进行连接 xff0c 而Android系统 xff0c 通过栈结构来保存整个App的Acti
  • Error: Invalid character in header content [“Authorization“]

    GET https xxxxxx com api getToken Body 34 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 eyJleHAiOjE2NzkzMTM5MDB9 VgZnHxBUqR3I PZD
  • 标准C++库有哪些

    C 43 43 标准库的内容基本可以分以下为10类 xff1a C1 标准库中与语言支持功能相关的头文件 C2 支持流输入 输出的头文件 C3 与诊断功能相关的头文件 C4 定义工具函数的头文件 C5 支持字符串处理的头文件 C6 定义容器
  • 示波器解析串口数据

    文章目录 前言一 示波器准备二 硬件介绍三 软件四 串口TTL数据协议1 波特率2 数据起始和停止信号3 数据有效位4 数据校验位 五 示波器探头连接五 串口数据波形捕获 方式1 xff1a 示波器自带的decode进行解码方式2 xff1
  • UART波形分析

    1 逻辑分析仪解码配置 波特率 xff1a 9600 2 逻辑分析仪结果 3 波特率计算 1除以9600 xff0c 结果如下 xff08 e 4表示10的负4次方 xff09 表示 0 00010416秒 61 104 16 微秒 找到发
  • snprintf()函数使用方法

    众所周知 sprintf不能检查目标字符串的长度 xff0c 可能造成众多安全问题 所以都会推荐使用snprintf 自从snprintf代替了sprintf xff0c 相信大家对snprintf的使用都不会少 xff0c 函数定义如下
  • vlc通过udp读取h264码流

    vlc通过udp读取h264码流 在使用gstreamer过程中 xff0c 遇到需要在服务端推送码流 xff0c 客户端接受的情况 xff0c 而有些主机并未安装gstreamer xff0c 但是可以采用vlc读取视频 xff0c 方法
  • ubuntu20.04回退系统内核

    ubuntu20 04回退系统内核 有时候开机之后突然发现nvidia smi检查不到驱动了 分辨率不对 xff0c 第二个屏幕检测不到等等 xff0c 一般来说是因为内核自动更新导致的 xff0c 这里介绍一下内核回退的方法 第一步 查看
  • Unity 重置项目资源的guid

    有时需要将多个项目的资源合并到一个项目中 xff0c 但有可能有些资源是共用的 xff0c 它的guid是一样的 xff0c 这样合并到一个项目时 xff0c 可能会超成冲突 如果要让每个资源的guid都不相同 xff0c 就需要重新生成g
  • zotero+logseq联合阅读文献具体设置

    title zotero 43 logseq联合阅读文献具体设置 date 2022 05 16 16 10 41 tags literature research logseq zotero zotero 43 logseq联合阅读文献具
  • hexo博客同时发布到github和gitee, 并使用gitee page action更新gitee page

    hexo博客同时发布到github和gitee xff0c 并使用gitee page action更新gitee page 发布博客到github page和gitee page 首先在github和gitee中各自建立一个公开仓库 xf
  • 在git bash中使用oh my zsh

    在git bash中使用oh my zsh 安装zsh 首先从官网下载git bash 进入MSYS2 Packages点击File下载 zst压缩包 xff0c 如图所示 xff1a 直接解压至Git根目录下 可通过git bash的快捷
  • 如何编写CMakeLists.txt,并且使用pkg-config

    这里记录一下如何编写CMakeLists txt 本篇blog记录如何在CMakeLists txt中使用pkg config 首先确定cmake版本 cmake minimum required VERSION 3 16 项目名称 pro
  • 解决WSL上不了网以及不能通过wsl网络ping通主机

    解决WSL上不了网以及不能通过wsl网络ping通主机 WSL上不了网 在更换电脑无线网络之后 xff0c 发现wsl上不了网 xff0c ssh显示no route xff0c ping baidu com ping不通 xff0c 但是
  • WIndows下cmd报错退出进程,代码为1

    WIndows下cmd报错退出进程 xff0c 代码为1 不知道什么原因出现了这种情况 参考微软官方回答 xff08 https answers microsoft com zh hans windows forum all cmd E6

随机推荐

  • docker使用load加载tar镜像时报错no such file or directory

    docker使用load加载tar镜像时报错no such file or directory 解决docker在使用load加载tar镜像时报错open var lib docker tmp docker import xxxxxxxxx
  • sudo启动的程序找不到动态库文件

    sudo启动的程序找不到动态库文件 在 bashrc中添加的LD LIBRARY PATH xff0c 并sudo ldconfig后 xff0c sudo启动的程序还是找不到依赖库 原因分析 sudo启动的程序不会用到bashrc中的配置
  • Pycharm显示cannot find declaration to go to,设置子目录为根目录

    Pycharm显示cannot find declartion to go to xff0c 设置子目录为根目录 使用Pycharm用ctrl跳转函数时显示cannot find declaration to go to 原因可能有很多 x
  • pycharm 2021.2.2 版本之前试用期过了怎么办

    pycharm 2021 2 2 版本之前试用期过了怎么办 虽然 jetbrains 的产品是商业收费 xff0c 而且价格不菲 xff0c 但官方还是为免费使用留下的空间 xff0c 实在良心 收费版可以免费试用30天 xff0c 问题是
  • layabox Native 自己下载资源并缓存

    我们在开发中 xff0c 不管是打的网络版还是本地版 xff0c 或多或少都有可能加载一些网络上的资源 xff0c 并且这些资源不想用dcc方式 xff0c 毕竟现在苹果对热更新管得比较严 xff0c 那如果不用dcc方式 xff0c 我们
  • Flutter 浅析之 自定义view 六 CircleProgressBar

    技术无止境 xff0c 只怕不学习啊 xff0c Flutter 我们开始吧 CircleProgressBar原型进度条 自定义view结合动画来完成进度条效果 CustomPainter 先来想想使用canvas的哪个方法来完成绘制 首
  • RoboMaster机甲大师:裁判系统服务器搭建(完全版)

    RoboMaster机甲大师 xff1a 裁判系统服务器搭建 xff08 完全版 xff09 更新 2022 03 28更新 2022 03 23前言准备搭建步骤MySQL安装配置环境组建局域网路由器端 Router 服务器端 Server
  • HTTP 请求方法 GET/POST/PUT/DELETE

    Web HTTP基础知识 HTTP请求是什么 xff1f HTTP超文本传输协议 xff0c 是确保服务器 xff08 Server xff09 和客户端 xff08 Client xff09 之间的正确通信 一个请求和响应的过程 xff1
  • kalibr标定工具使用方法

    1 首先在docker中安装ubuntu14 04 在16 04编译不过 xff0c 不知道为什么 xff0e 2 安装kalibr ros包 xff0e 3 下载官方提供的验证数据 xff0e 4 我们先标定相机 xff0c 使用的数据为
  • C语言、C++ 和 C# 三者的区别

    按时间顺序说一说这三门语言的基本吧 xff0c 这样比较一下儿就能知道他们的区别了 一 xff1a xff23 语言 xff23 语言诞生得非常早 xff0c 当时人们普遍还习惯用汇编语言编写软件 xff0c 而且没有什么统一 xff0c
  • requests的代理使用

    import requests from lxml import etree headers 61 39 User Agent 39 39 Mozilla 5 0 Macintosh Intel Mac OS X 10 15 7 Apple
  • HiMPP SAMPLE_VENC分析

    mpp中的例程 每一个例程面向一个典型应用 xff0c common是通用性主体函数 xff0c 我们只分析vencvenc中的main调用venc中的功能函数 xff0c 再调用common中的功能函数 xff0c 再调用mpp中的API
  • H264数据格式解析

    什么是H 264 H264 是 MPEG 4 标准所定义的最新编码格式 xff0c 同时也是技术含量最高 代表最新技术水平的视频编码格式之一 xff0c 标准写法应该是H 264H264 视频格式是经过有损压缩的 xff0c 但在技术上尽可
  • ISP和IQ调试

    什么是ISP isp image signal process 图像信号处理 xff0c 这是技术image signal processor 图像信号处理器 这是设备本质 xff1a 通过数字运算来修补前端采集的不理想数据 xff0c 尽
  • cortex-m3中寄存器

    简介 Cortex M3 是一个 32 位处理器内核 内部的数据路径是 32 位的 xff0c 寄存器是 32 位的 xff0c 存储器接 口也是 32 位的 CM3 采用了哈佛结构 xff0c 拥有独立的指令总线和数据总线 xff0c 可
  • RGBD-SLAM(一)——深度摄像机

    工欲善其事必先利其器 我们先从能够获取RGBD数据的相机开始谈起 首先我们来看一看其分类 一 根据其工作原理主要分为三类 xff1a 1 双目方案 xff1a xff08 1 xff09 原理 xff1a http blog csdn ne
  • 软件执行的流程

    什么是编译原理 代码执行分为2个部分 compilers 编译器 1 将代码编译成可执行文件 xff0c 汇编代码或者字节码 xff0c MDK就是一个编译器interpreters 解释器 1 执行编译器生成的可执行文件 xff0c mc
  • 编译器过程概述

    Lexical Analysis xff08 词法分析 xff09 词法分析的目的是将程序文本划分为单词 xff0c 或者我们在编译器中所说的 xff0c 标记利用程序文本中的空格 xff0c 分号等符号来识别程序文本 Parsing xf
  • 编程语言的一些答疑

    为什么会有这么多中编程语言 因为需求是多种多样的 xff0c 为一种语言实现所有需求是非常困难的 为什么会有新的编程语言 实现一个新的编译器并不困难 xff0c 几个人就可以了 xff0c 大型的编译器可能也就十几个人 xff0c 真正的成
  • 【串口系列】不定长接收多种方式

    目录 背景 声明 开发环境 正文 一 接收中断 43 空闲中断 二 接收中断 43 T35定时器中断 T35定时器 三 空闲中断 43 DMA 43 循环队列 背景 在单片机开发过程中 xff0c 串口通讯是一种非常常用的串行通通讯方式 x