stm32串口+DMA环形缓冲收发保姆级

2023-05-16

基于HAL库的STM32串口DMA环形缓冲收发实例

首先在此感谢开源项目,以及大佬们的无私奉献,让每一个逐梦人能够免费学习,再次感谢!
发布只为记录,记性不够,笔记来凑。记得点赞哦
具体实现原理讲起来确实挺复杂,不过用起来还是很NICE的!可以直接移植!

1、STM32CubeMax配置

1.1、选择单片机型号
在这里插入图片描述

2、配置时钟和串口

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

或者直接在HCLK位置输入72,点击OK自动配置

在这里插入图片描述
在这里插入图片描述

这个地方第四步,模式选择MODE。发送选择正常NOMAL.接收RX选择循环模式,第五步,外设地址不自增,存储器地址自增勾选

数字长度选择字节模式byte

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

此处必须使能UART,原因后面会提到

然后点击生成文件就行。如果用的keil,则直接点击Open Project,如果用的VSCODE,选择打开文件夹Open folder

在这里插入图片描述

在这里插入图片描述

到这个页面后点击MDK_ARM,就是生成的文件

然后返回上一层,右击,选择打开方式,为通过code打开,


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
右击,选择新建文件夹在这里插入图片描述

新建文件夹BSP 和bsp_usart.h和bsp_usart.c

将文件夹拖到目录栏

在这里插入图片描述

在这里插入图片描述

将刚才的两个文件夹路径放进来在这里插入图片描述

至此,所有的构建操作已经全部完成,剩下的就是程序编辑

为了方便打印调试,引用printf打印输出,引入fputc,添加头文件#include “stdio.h”

#include "stdio.h"
int fputc(int ch, FILE *f)
{
    // while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
    // USART1->DR = (unsigned char) ch;
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
    return ch;
}

huart1结构体句柄由系统自动生成


保姆级的提示到这一步结束,要是不会用就直接调用吧,下来可能就是关键步骤,不会这么详细的讲,至于如何使用这些引入的函数,点击这个链接

定义数组,用来存放发送的数据

#define UART1_RX_RB_LEN (128u)

uint8_t usart1_tx_rb_data[128];
uint8_t usart1_rx_rb_data[UART1_RX_RB_LEN];

绑定结构体指针BUFF和定义的实体数组

lwrb_init(&usart1_tx_rb, usart1_tx_rb_data, ARRAY_LEN(usart1_tx_rb_data));
lwrb_init(&usart1_rx_rb, usart1_rx_rb_data, ARRAY_LEN(usart1_rx_rb_data));

2、发送环节

​ 为了移植方便,使底层逻辑不暴露,引入发送函数,方便外部调用

void USART1_SendData(const uint8_t *p_data, uint16_t len)
{
    lwrb_write(&usart1_tx_rb, p_data, len); /* Write data to TX buffer for loopback */
    USART1_Start_DmaTx();
}

void USART1_Sendstring(const char *str)
{
    lwrb_write(&usart1_tx_rb, str, strlen(str)); /* Write data to TX buffer for loopback */
    USART1_Start_DmaTx();

}

真正的发送函数处理

uint8_t USART1_Start_DmaTx(void)
{
    uint32_t primask;
    uint8_t started = 0;

    primask = __get_PRIMASK();
    __disable_irq();
    if (usart1_tx_dma_current_len == 0
            && (usart1_tx_dma_current_len = lwrb_get_linear_block_read_length(&usart1_tx_rb)) > 0) 
    {
        __HAL_DMA_DISABLE(&hdma_usart1_tx);
         
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_usart1_tx));
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_HT_FLAG_INDEX(&hdma_usart1_tx));
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_TE_FLAG_INDEX(&hdma_usart1_tx));
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_GI_FLAG_INDEX(&hdma_usart1_tx));

        HAL_UART_Transmit_DMA(&huart1
            , (uint8_t*)lwrb_get_linear_block_read_address(&usart1_tx_rb)
            , (uint16_t)usart1_tx_dma_current_len);
        started = 1;
    }
    __set_PRIMASK(primask);
    
    return started;
}

发送完成后触发中断,跳过当前已经有的长度,然后再次进入发送状态,判断缓冲区中是否还存在数据,如果有进入IF语句,如果没有,返回started = 0;此时发送形成一个循环发送和检测

void USART1_TxTcCb(UART_HandleTypeDef *huart)
{
        lwrb_skip(&usart1_tx_rb, usart1_tx_dma_current_len);/* Skip sent data, mark as read */
        usart1_tx_dma_current_len = 0;           /* Clear length variable */
        USART1_Start_DmaTx();          /* Start sending more data */
}

3、接收环节

HAL_UARTEx_ReceiveToIdle_DMA(&huart1, usart1_rx_dma_buffer, ARRAY_LEN(usart1_rx_dma_buffer));

利用此函数可以接受数据,直至触发IDLE空闲中断,具体细节点击函数查看,不在此赘述

此时会产生三种情况,接受一半中断。接受溢出中断和接收完成后空闲中断

如果自己注册回调函数则,会调用自己的回调函数,不然则为系统默认回调函数

不会的可以参考此篇注册串口回调函数

//注册回调事件
HAL_UART_RegisterRxEventCallback(&huart1, USART1_RxEventCb);

HAL_UART_RegisterCallback(&huart1, HAL_UART_TX_COMPLETE_CB_ID, USART1_TxTcCb);

产生回调后,进入回调函数,可以自定义一些回调处理和动作

//接受半个缓冲区完成回调函数
void USART1_DMA_RxHtCb( DMA_HandleTypeDef * p_hdma)
{
    UNUSED(p_hdma);

    USART1_RxEventCheck();
}

//接收完成回调函数
void USART1_DMA_RxTcCb( DMA_HandleTypeDef * p_hdma)
{
    UNUSED(p_hdma);
    
    USART1_RxEventCheck();
	usart1_rx_flag = 1;
}

/*!< UART Reception Event Callback     */
void USART1_RxEventCb(struct __UART_HandleTypeDef *huart, uint16_t pos)
{
    UNUSED(huart);
    UNUSED(pos);
    
    USART1_RxEventCheck();
}

对回调事件进行检查分析,看是那种情况,具体参考大佬文章

在这里插入图片描述

共有这么几种情况:

  • 情况A:缓冲区为空W == R = 0 == 0

  • 情况B:缓冲区将字节保存为W - R = 4 - 0 = 4``W > R

  • Case C : 缓冲区已满or orW == R - 1``7 == 0 - 1``7 = (0 - 1 + S) % S = (0 - 1 + 8) % 8 = (-1 + 8) % 8 = 7

    • R并且W可以保存S不同的值,从0到,即模数S - 1``S
    • 缓冲区将字节保存为W - R = 7 - 0 = 7``W > R
  • 情况D:缓冲区将字节保存为S - (R - W) = 8 - (5 - 3) = 6``R > W

  • 情况E:缓冲区已满为( ) 并保存字节W == R - 1``4 = 5 - 1``S - (R - W) = 8 - (5 - 4) ) = 7}

void USART1_RxEventCheck(void)
{
    static uint16_t old_pos;
    uint16_t pos;
    
    /* Calculate current position in buffer and check for new data available */
    //检查缓冲区中已用长度
    pos = ARRAY_LEN(usart1_rx_dma_buffer) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
    if (pos != old_pos) 
    {    /* Check change in received data */
        if (pos > old_pos) 
        { /* Current position is over previous one */
            /*
             * Processing is done in "linear" mode.
             *
             * Application processing is fast with single data block,
             * length is simply calculated by subtracting pointers
             *
             * [   0   ]
             * [   1   ] <- old_pos |------------------------------------|
             * [   2   ]            |                                    |
             * [   3   ]            | Single block (len = pos - old_pos) |
             * [   4   ]            |                                    |
             * [   5   ]            |------------------------------------|
             * [   6   ] <- pos
             * [   7   ]
             * [ N - 1 ]
             */

            lwrb_write(&usart1_rx_rb, &usart1_rx_dma_buffer[old_pos], pos - old_pos);
        } 
        else 
        {
            /*
             * Processing is done in "overflow" mode..
             *
             * Application must process data twice,
             * since there are 2 linear memory blocks to handle
             *
             * [   0   ]            |---------------------------------|
             * [   1   ]            | Second block (len = pos)        |
             * [   2   ]            |---------------------------------|
             * [   3   ] <- pos
             * [   4   ] <- old_pos |---------------------------------|
             * [   5   ]            |                                 |
             * [   6   ]            | First block (len = N - old_pos) |
             * [   7   ]            |                                 |
             * [ N - 1 ]            |---------------------------------|
             */
            lwrb_write(&usart1_rx_rb, &usart1_rx_dma_buffer[old_pos], ARRAY_LEN(usart1_rx_dma_buffer) - old_pos);
            
            if (pos > 0) 
            {
                lwrb_write(&usart1_rx_rb, &usart1_rx_dma_buffer[0], pos);
            }
        }
         old_pos = pos; /* Save current position as old for next transfers */
		usart1_rx_flag = 1;

    }
}

当标志位置一后,对读取到的数据进行处理,此时传进来的数据需要我们自行进行拼接,比如上述情况E,发送的数据存到了两端,这样需要先将后面的读出来,再加上前面的

void USART1_ProcessData(void)
{
    uint8_t data[UART1_RX_RB_LEN];
    uint16_t len1 = lwrb_get_linear_block_read_length(&usart1_rx_rb);
    printf("len1 = %d\r\n", len1);
    if (len1 > 0) 
    {
        printf("lwrb_read(&usart1_tx_rb, data, len1) = %d\r\n"
            , lwrb_read(&usart1_rx_rb, data, len1));
        uint16_t len2 = lwrb_get_linear_block_read_length(&usart1_rx_rb);
        printf("len2 = %d\r\n", len2);
        if (len2 > 0)
        {
            printf("lwrb_read(&usart1_tx_rb, &data[len1], len2) = %d\r\n"
                , lwrb_read(&usart1_rx_rb, &data[len1], len2));
        }
        // 进行data进行处理

        // 处理usart1_rx_rb的一些东西
        lwrb_write(&usart1_tx_rb
            , data
            , len1 + len2); /* Write data to TX buffer for loopback */  

        USART1_Start_DmaTx();
    }
}

再main函数中对标志位进行处理,

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_Delay(50);
  USART1_Init();
  printf("stm32f103rct6_uart_dma_loopback_test!\r\n");
  // uint8_t str[] = "USART1_Init\r\n";
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (usart1_rx_flag == 1)
    {
      usart1_rx_flag = 0;
      // printf("usart1_rx_flag = 1\r\n");
      USART1_ProcessData();
    }
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

下面附上BSP_USART.c完整代码,


#include "bsp_usart.h"
#include "usart.h"
#include "dma.h"

#define UART1_RX_DMA_BUFFER_LEN (20u)
#define UART1_RX_RB_LEN (128u)

lwrb_t usart1_rx_rb;// Ring buffer instance for TX data
lwrb_t usart1_tx_rb;// Ring buffer instance for TX data

uint8_t usart1_rx_dma_buffer[UART1_RX_DMA_BUFFER_LEN];
uint8_t usart1_rx_rb_data[UART1_RX_RB_LEN];// Ring buffer data array for RX DMA
uint8_t usart1_tx_rb_data[128];// Ring buffer data array for TX DMA

volatile size_t usart1_tx_dma_current_len;// Length of currently active TX DMA transfer
volatile uint8_t usart1_rx_flag;

int fputc(int ch, FILE *f)
{

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);

    return ch;
}

uint8_t USART1_Start_DmaTx(void)
{
    uint32_t primask;
    uint8_t started = 0;
    
    primask = __get_PRIMASK();
    __disable_irq();

    if (usart1_tx_dma_current_len == 0
            && (usart1_tx_dma_current_len = lwrb_get_linear_block_read_length(&usart1_tx_rb)) > 0) 
    {
        __HAL_DMA_DISABLE(&hdma_usart1_tx);

        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_usart1_tx));
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_HT_FLAG_INDEX(&hdma_usart1_tx));
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_TE_FLAG_INDEX(&hdma_usart1_tx));
        __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx
            , __HAL_DMA_GET_GI_FLAG_INDEX(&hdma_usart1_tx));

        HAL_UART_Transmit_DMA(&huart1
            , (uint8_t*)lwrb_get_linear_block_read_address(&usart1_tx_rb)
            , (uint16_t)usart1_tx_dma_current_len);

        started = 1;
    }
    __set_PRIMASK(primask);
    
    return started;
}

void USART1_DMA_RxHtCb( DMA_HandleTypeDef * p_hdma)
{
    UNUSED(p_hdma);
    
    USART1_RxEventCheck();
}

//接收完成回调函数
void USART1_DMA_RxTcCb( DMA_HandleTypeDef * p_hdma)
{
    UNUSED(p_hdma);
    
    USART1_RxEventCheck();
	usart1_rx_flag = 1;
}

void USART1_RxEventCb(struct __UART_HandleTypeDef *huart, uint16_t pos)
{
    UNUSED(huart);
    UNUSED(pos);

    USART1_RxEventCheck();
}

// 串口1发送完成回调
void USART1_TxTcCb(UART_HandleTypeDef *huart)
{
        lwrb_skip(&usart1_tx_rb, usart1_tx_dma_current_len);	/* Skip sent data, mark as read */
        usart1_tx_dma_current_len = 0;          			   /* Clear length variable */
        USART1_Start_DmaTx();         					       /* Start sending more data */
}

void USART1_Init(void)
{
    lwrb_init(&usart1_tx_rb, usart1_tx_rb_data, ARRAY_LEN(usart1_tx_rb_data));
    lwrb_init(&usart1_rx_rb, usart1_rx_rb_data, ARRAY_LEN(usart1_rx_rb_data));

    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, usart1_rx_dma_buffer, ARRAY_LEN(usart1_rx_dma_buffer));
    HAL_UART_RegisterRxEventCallback(&huart1, USART1_RxEventCb);
    HAL_UART_RegisterCallback(&huart1, HAL_UART_TX_COMPLETE_CB_ID, USART1_TxTcCb);
}

// void USART1_RxEventCheck(uint16_t pos)
void USART1_RxEventCheck(void)
{
    static uint16_t old_pos;
    uint16_t pos;
    
    /* Calculate current position in buffer and check for new data available */
    //检查缓冲区中已用长度
    pos = ARRAY_LEN(usart1_rx_dma_buffer) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
    // printf("old_pos = %d, pos = %d\r\n", old_pos, pos);
    if (pos != old_pos) 
    {    /* Check change in received data */
        if (pos > old_pos) 
        { /* Current position is over previous one */
            /*
             * Processing is done in "linear" mode.
             *
             * Application processing is fast with single data block,
             * length is simply calculated by subtracting pointers
             *
             * [   0   ]
             * [   1   ] <- old_pos |------------------------------------|
             * [   2   ]            |                                    |
             * [   3   ]            | Single block (len = pos - old_pos) |
             * [   4   ]            |                                    |
             * [   5   ]            |------------------------------------|
             * [   6   ] <- pos
             * [   7   ]
             * [ N - 1 ]
             */

            lwrb_write(&usart1_rx_rb, &usart1_rx_dma_buffer[old_pos], pos - old_pos);
        } 
        else 
        {
            /*
             * Processing is done in "overflow" mode..
             *
             * Application must process data twice,
             * since there are 2 linear memory blocks to handle
             *
             * [   0   ]            |---------------------------------|
             * [   1   ]            | Second block (len = pos)        |
             * [   2   ]            |---------------------------------|
             * [   3   ] <- pos
             * [   4   ] <- old_pos |---------------------------------|
             * [   5   ]            |                                 |
             * [   6   ]            | First block (len = N - old_pos) |
             * [   7   ]            |                                 |
             * [ N - 1 ]            |---------------------------------|
             */

            lwrb_write(&usart1_rx_rb, &usart1_rx_dma_buffer[old_pos], ARRAY_LEN(usart1_rx_dma_buffer) - old_pos);
            
            if (pos > 0) 
            {
            
                lwrb_write(&usart1_rx_rb, &usart1_rx_dma_buffer[0], pos);
            }
        }
        old_pos = pos; /* Save current position as old for next transfers */
	    usart1_rx_flag = 1;
    }
}

void USART1_ProcessData(void)
{
    uint8_t data[UART1_RX_RB_LEN];
    uint16_t len1 = lwrb_get_linear_block_read_length(&usart1_rx_rb);
    printf("len1 = %d\r\n", len1);
    if (len1 > 0) 
    {
        printf("lwrb_read(&usart1_tx_rb, data, len1) = %d\r\n"
            , lwrb_read(&usart1_rx_rb, data, len1));
        uint16_t len2 = lwrb_get_linear_block_read_length(&usart1_rx_rb);
        printf("len2 = %d\r\n", len2);
        if (len2 > 0)
        {
            printf("lwrb_read(&usart1_tx_rb, &data[len1], len2) = %d\r\n"
                , lwrb_read(&usart1_rx_rb, &data[len1], len2));
        }
        // 进行data进行处理

        // 处理usart1_rx_rb的一些东西
        lwrb_write(&usart1_tx_rb
            , data
            , len1 + len2); /* Write data to TX buffer for loopback */  

        USART1_Start_DmaTx();
    }
}

void USART1_SendData(const uint8_t *p_data, uint16_t len)
{
    lwrb_write(&usart1_tx_rb, p_data, len); /* Write data to TX buffer for loopback */
    USART1_Start_DmaTx();
}

void USART1_Sendstring(const char *str)
{

    lwrb_write(&usart1_tx_rb, str, strlen(str)); /* Write data to TX buffer for loopback */
    USART1_Start_DmaTx();

}



BSP_USART_H文件

#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "main.h"
#include "stdio.h"
#include "lwrb.h"

#define ARRAY_LEN(x)            (sizeof(x) / sizeof((x)[0]))


extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern volatile size_t usart1_tx_dma_current_len;
extern volatile uint8_t usart1_rx_flag;

void USART1_Init(void);
void USART1_ProcessData(void);
void USART1_SendData(const uint8_t *p_data, uint16_t len);
void USART1_Sendstring(const char* str);










#endif // ! __BSP_USART_H__

lwrb文件链接

#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "main.h"
#include "stdio.h"
#include "lwrb.h"

#define ARRAY_LEN(x)            (sizeof(x) / sizeof((x)[0]))


extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern volatile size_t usart1_tx_dma_current_len;
extern volatile uint8_t usart1_rx_flag;

void USART1_Init(void);
void USART1_ProcessData(void);
void USART1_SendData(const uint8_t *p_data, uint16_t len);
void USART1_Sendstring(const char* str);


#endif // ! __BSP_USART_H__

lwrb文件[链接](https://github.com/MaJerle/lwrb)

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

stm32串口+DMA环形缓冲收发保姆级 的相关文章

  • 1天精通Apipost--全网最全gRPC调试和智能Mock讲解!

    gRPC 接口调试 grpc 作为一个老程序员 xff0c 最近公司技术架构用到了gPRC xff0c 但国内很少有支持这个的工具 xff0c 大部分都只是支持http 由于我同时也是Apipost骨灰级用户 xff0c 于是就在他们官网的
  • CAN总线波特率的设定——以STM32为例

    波特率的设定 首先是几个名词的含义 xff0c CAN里面1个位的构成如下 注意采样点的位置在PBS1和PBS2的中间 根据这个位时序就可以计算波特率了 最小时间单位 xff08 Tq xff0c Time Quantum xff09 同步
  • 2021电赛备赛心路历程(含代码例程)

    作为一个电子学院学生 xff0c 大二暑假才开始自学单片机知识 xff08 还是因为报名了电赛而不得不去学 xff09 xff0c 深感愧疚 从今年7月至8 4的将近四周时间内哩哩啦啦学了一些基础模块 xff08 其中光是练习点灯和其他基础
  • 20201114-三轴云台storm32 BGC HAKRC调试+

    storm32 BGC HAKRC 2轴云台支持俯仰 xff08 抬头低头 xff09 以及横滚 xff1b 三轴多了一个航向 支持锁头模式 xff0c 拍摄更方便 可以控制俯仰通过接收机或者其他单独PWM通道 可以设置跟随模式或者锁定模式
  • KEIL5中头文件路劲包含问题

    方式1 xff1a 1 Keil中添加头文件相对路劲的方法 在c c 43 43 配置中添加路劲 xff0c 最终是将添加的绝对路径转化为相对路径 xff1b 注意 xff1a 相对路径的当前位置指 uvproj文件所在位置 在C C 43
  • php curl函数应用方法之模拟浏览器

    curl 是使用URL语法的传送文件工具 xff0c 支持FTP FTPS HTTP HTPPS SCP SFTP TFTP TELNET DICT FILE和LDAP curl 支持SSL证书 HTTP POST HTTP PUT FTP
  • WireShark基本抓包数据分析

    WireShark抓包数据分析 xff1a 1 TCP报文格式 源端口 目的端口 xff1a 16位长 标识出远端和本地的端口号 顺序号 xff1a 32位长 表明了发送的数据报的顺序 确认号 xff1a 32位长 希望收到的下一个数据报的
  • VScode下运行调试C++文件

    1 下载vscode 这个可以直接去官网下载 2 下载mingw64 下载mingw64就是下载C 43 43 的编译器g 43 43 和调试器gdb 这个也可以去官网下载 xff0c 下载安装完成后去配置环境变量 我将mingw64安装在
  • c++模板的优点和缺点

    作为C 43 43 语言的新组成部分 xff0c 模板引入了基于通用编程的概念 通用编程是一种无须考虑特定对象的描述和发展算法的方法 xff0c 因此它与具体数据结构无关 但在决定使用C 43 43 模板之前 xff0c 让我们分析一下使用
  • 基于导航网格的A星寻路(Navigation mesh)

    最近花了几个月的时间实现了导航网格寻路和导航网格自动生成 导航网格数据结构定义 由于数据之间有着层级关系 xff0c 所以采用XML进行定义 navmesh基本元素 xff1a 顶点 Verts 43 可走边 Edges 43 凸多边形 P
  • cmake 引入 动态库、静态库

    动态库 xff1a link directories PROJECT SOURCE DIR lib 添加动态连接库的路径 target link libraries project name lMNN 添加libMNN so 静态库 xff
  • 利用JSP内置对象Request和Application实现用户名密码登录注册进入首页显示

    学习目标 xff1a 实验名称 xff1a JSP内置对象目的 xff1a 掌握JSP页面的全部语法 能够编写基本的JSP网页 学习内容 xff1a 1 实验 地点 周三2单元 xff0c 10617综合一实验室 自带电脑 目的 掌握各种内
  • 【如何写CMake】一个解决方案,多个工程

    文章目录 代码参考 一个解决方案 xff0c 多个工程 xff0c 即多个exe dll lib等 代码 多加几个ADD EXECUTABLE即可 1 cmake verson xff0c 指定cmake版本 cmake minimum r
  • 指针、寄存器、位操作

    定义寄存器的绝对地址 xff0c 并转换为指针进行位操作 1 位操作示例一 define PERIPH BASE unsigned int 0x40000000 define APB2PERIPH BASE PERIPH BASE 43 0
  • 详解TCP连接的建立

    TCP首部格式 TCP报文段首部的前20个字节是固定的 xff0c 后面有4N字节是根据需要而增加的选项 xff0c 因此TCP报文段的最小长度为20字节 首部固定部分的各字段的意义如下 xff1a 1 源端口和目的端口 xff1a 加上I
  • printf打印函数的原理浅析

    printf的底层原理浅析 目录 printf的底层原理浅析前言函数变参格式解析一个简单的printf示例结语 补充 前言 最近在学习linux内核的时候用到了自定义实现的printf xff0c 学习了一下 xff0c 在此记录 xff0
  • 基于公开网站挖掘敏感信息的研究与分析- Fofa 搜索

    基于公开公开网站挖掘敏感信息的研究与分析 Fofa 搜索 一 引言 1 1项目概述 基于公开网站的敏感信息挖掘研究与分析 xff1a 针对目前网络安全整体的趋势我们从google等搜索引擎 Github等代码库 FOFA等空间搜索这三个方面
  • HTTP报文

    一 HTTP报文的结构 用于HTTP协议交互的信息 xff0c 称为HTTP报文 客户端的HTTP报文称为请求报文 xff0c 服务端的称为响应报文 HTTP报文结构如下图 xff1a 下面是请求报文的一个实例 xff1a 请求行 xff1
  • 用C++写一个UDP发送和接收程序

    发送程序Sender cpp include lt stdio h gt include lt string gt include lt iostream gt include lt winsock h gt using namespace
  • STM32自学笔记(五)串口通信

    xff08 想要深入理解就把前面的看下 xff0c 否则直接看使用总结即可 xff09 usart文件夹 usart 文件夹内包含了 usart c和usart h两个文件 这两个文件用于串口的初始化和中断接收 代码只针对了串口1 xff0

随机推荐

  • sockaddr_in详解

    struct sockaddr in short sin family Address family一般来说AF INET xff08 地址族 xff09 PF INET xff08 协议族 xff09 unsigned short sin
  • ubuntu下好用的TCP/UDP调试工具

    github官方链接 GitHub s kyo mNetAssist mNetAssist A UDP TCP Assistant 编译好的安装包 ubuntu下好用的TCP UDP调试工具 网络设备文档类资源 CSDN下载 1 解压dpk
  • Linux网络编程之connect函数分析

    在一个 CLIENT SERVER模型的网络应用中 xff0c 客户端的调用序列大致如下 xff1a socket gt connect gt recv send gt close 其中socket没有什么可疑问的 xff0c 主要是创建一
  • linux route 命令

    显示现在所有路由 route n root 64 Ubuntu route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 10
  • 大量LAST_ACK 分析过程

    记录一下自己的思想过程 现象 在netstat的时候发现大量处于LAST ACK状态的TCP连接 xff0c 达到在ESTABLISHED状态的90 以上 root 64 ccsafe netstat ant fgrep 34 34 cut
  • const 与重载

    const到底是不是一个重载的参考对象 xff0c 请看下面的例子 class A public void f int i std cout lt lt 34 1 34 函数1 void f int i const std cout lt
  • 指向成员函数的指针

    指向成员函数的指针 取一个非静态成员函数的地址 xff0c 如果该函数是nonvirtual xff0c 则得到的结果是它在内存中真正的地址 然而这个值也不是完全的 xff0c 它也需要被绑定于某个class object的地址上 xff0
  • rviz 远程显示及控制移动机器人的导航

    环境 xff1a xff08 1 xff09 虚拟机本地端IP 192 168 10 30 xff08 虚拟机需要通过桥接的形式与PC本地机连接 xff0c PC 机IP 192 168 10 21 xff09 xff08 2 xff09
  • iptables - administration tools for packet filtering and NAT

    2 iptables administration tools for packet filtering and NAT Linux Iptables Manual Incoming Traffic V 43 43 PREROUTING 4
  • C++ 内部类、嵌套类

    1 嵌套类 nested class 一个类可以定义在另一个类的内部 xff0c 前者称为嵌套类 xff0c 嵌套类时一个独立的类 xff0c 和外层类基本没什么关系 它通过提供新的类型类作用域来避免名称混乱 嵌套类必须声明在类的内部 xf
  • SONY VAIO P VPCP118KJ索尼酷袋本 鸡肋上网本初体验

    随着消费者市场需求的不断多样化和个性化 xff0c 个人电脑厂家开始不断突破创新 xff0c 在产品线拼装出各种各样的个人电脑 xff0c 主打性能的游戏本 xff0c 主打便携轻薄本 xff0c 2009年的CES上索大法推出了一款与众不
  • 第2课【CMSIS和标准库开发】标准库 CMSIS 分层

    目录 基本知识框架课堂笔记CMSISCMSIS的分层关系和作用CMSIS层文件结构内核函数层文件外设函数访问层文件 标准库开发标准库开发 LED GPIO标准库开发 KEY GPIO标准库开发 位带操作位带位带位带别名区位带区和位带别名区的
  • 第4课【STM32的时钟】时钟 时钟源 内外部时钟 高低速时钟

    目录 基本知识框架课堂笔记时钟什么是时钟 xff1f 时钟有什么作用 时钟源HSE 外部高速时钟HSI 内部高速时钟LSE 外部低速时钟LSI 内部低速时钟PLL锁相环 主要时钟和其他时钟主要时钟其他时钟 配置系统时钟实验 基本知识框架Xm
  • 第7课【SysTick定时器】中断 系统定时器 寄存器

    目录 基本知识框架课堂笔记SysTick定时器简介什么是SysTick定时器SysTick定时器的作用 SysTick寄存器CTRL控制及状态寄存器LOAD重装载数值寄存器VALUE当前数值寄存器CALIB校准数值寄存器 SysTick实战
  • 第8课【通讯的基本概念】串行并行 单工半双工全双工 同步异步 比特率波特率

    目录 基本知识框架课堂笔记通讯传送方式串行通讯并行通讯串行通讯与并行通讯的特性 通讯方向单工通讯半双工通讯全双工通讯 同步方式同步通讯异步通讯同步通讯与异步通讯的区别 传输单位比特率波特率比特率和波特率的联系 通讯简介通讯标准 基本知识框架
  • 嵌入式宏定义中do...while(0)的意义

    目录 背景do while 0 形式宏定义使得宏定义拥有一些函数的特点 分析1 封装 xff1a 使得宏定义可以包含复杂的内容而不容易出错 xff0c 提高代码健壮性2 生命周期 xff1a 宏定义内 外部定义的同名变量不会冲突3 返回 x
  • 第9课【USB协议】USB总线 接口 端点 管道 数据包 枚举 STM32_USB-FS-Device_Lib V4.1.0

    目录 简介背景特点版本 数据流模型总线拓扑结构主 从设备通信流端点管道 物理层接口类型USB Type AUSB Type BUSB Type C 引脚定义4PIN9PIN5PIN24PIN16PIN6PIN 电平标准 协议层 通讯方式通讯
  • 第10课【STM32 USB通讯协议实战】HID键盘+CDC虚拟串口组合设备

    目录 前言USB设备类别未定义设备设备描述符 配置描述符分析配置从机类型配置设备专用的描述符配置从机端点实现设备类特定请求 HID设备特点HID键盘描述符实例HID报文描述符短条目报文生成HID键盘报文描述符实例总结 CDC设备特点CDC
  • 创建 std_srvs::Empty 型 Service (参数为空的服务)

    参考 xff1a ros NodeHandle advertiseService API docs 1 包含头文件 include lt std srvs Empty h gt 2 创建服务 xff0c 并绑定服务的回调函数 restart
  • stm32串口+DMA环形缓冲收发保姆级

    基于HAL库的STM32串口DMA环形缓冲收发实例 首先在此感谢开源项目 xff0c 以及大佬们的无私奉献 xff0c 让每一个逐梦人能够免费学习 xff0c 再次感谢 xff01 发布只为记录 xff0c 记性不够 xff0c 笔记来凑