RTOS队列的写入与读出

news2024/11/26 21:29:14

      我们在stm32f103c8t6单片机上验证RTOS队列的写入与读出,利用stm32cube进行RTOS的配置。在选择TIM2当做RTOS的时钟,裸机的时钟源默认是 SysTick,但是开启 FreeRTOS 后,FreeRTOS会占用 SysTick (用来生成1ms 定时,用于任务调度),所以需要需要为其他总线提供另外的时钟源。

验证的功能比较简单,选择V1 版本的内核完全够用。

一、验证的思路及需用函数及队列使用的说明

1.创建2个任务taskKEY1,taskKEY2。

任务要求如下:

taskKEY1:按下KEY1调用xQueueSend()函数,发送数据。发送成功,显示发送的数据;发送失败,显示写入队列失败。

taskKEY2:按下KEY2调用xQueueReceive()函数,读取数据。读取成功,显示读取的数据;读取失败,显示读取队列失败。

2.需要的函数

创建队列

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

uxQueueLength:队列可同时容纳的最大项目数 。

uxItemSize:存储队列中的每个数据项所需的大小(以字节为单位)。 返回值: 如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法分配 , 则返回 NULL。

写队列

BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

xQueue:队列的句柄,数据项将发送到此队列

pvItemToQueue:待写入数据

xTicksToWait:阻塞超时时间

如果成功写入数据,返回 pdTRUE,否则返回 errQUEUE_FULL。

该函数会往队列的尾部写入消息,当你写入的数据超过了队列长度或者在阻塞时间内没有写入数据就会写入失败

读队列

BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait )

xQueue:待读取的队列

pvItemToQueue:数据读取缓冲区

xTicksToWait:阻塞超时时间

返回值: 成功返回 pdTRUE,否则返回 pdFALSE。

该函数会从队列头部读取消息,并删除消息,当你读出的的数据超过了写入的数据或者在阻塞时间内没有读出数据就会读出失败

3. 队列使用的说明

  队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息。

队列项目:队列中的每一个数据;(打个预防针,每一个的理解不是真的就一个,这篇文章的代码部分freertos.c那里我会说明。)

队列长度:队列能够存储队列项目的最大数量;

创建队列时,需要指定队列长度及队列项目大小。这个在我们使用stm32cube配置的时候会提到。

队列的特点

①通常采用先进先出(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取。 也可以配置为后进先出(LIFO)方式,但用得比较少。FIFO就是First Input First Output

②采用实际值传递,即将数据拷贝到队列中进行传递,也可以传递指针,在传递较大的数据的时候 采用指针传递。我们的读写队列函数里面的数据参数就是指针类型。

③队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息

④当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队。 阻塞时间如果设置为: 0:直接返回不会等待;

0~port_MAX_DELAY:等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不 再等待;

port_MAX_DELAY:死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;

二、stm32cube的配置

SYS

RCC

GPIO

PA0对应按键1,PA1对应按键2。

RTOS

在Tasks and Queues中配置我们的2个任务,一个队列

其实就是相当于stm32cube帮我们调用了并封装了生成任务xTaskCreate(),生成队列xQueueCreate函数。

两个任务和一个队列的名字分别是

TaskSend,TaskReceive, myQueue

两个任务的入口函数名字分别是

StartSend,StartReceive;

各参数的配置相同,如下图

任务1和2除了任务名字和入口函数不同,其余的都相同

三、代码部分

usart.c

#include "stdio.h"

int fputc(int ch, FILE *f)

{

unsigned char temp[1]={ch};

HAL_UART_Transmit(&huart1,temp,1,0xffff);

return ch;

}

同时打开“魔术棒”,勾选Use MicroLIB,点击OK。这样就可以进行串口打印了。

在freertos.c里我要分两种情况给大家说明创建队列的时候队列项目和队列长度

队列项目:队列中的每一个数据;

队列长度:队列能够存储队列项目的最大数量;

freertos.c

首先来验证一次性发送一个数据,读一个数据

#include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "stdio.h"

//#include "queue.h"

//QueueHandle_t queue;

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

osThreadId TaskSendHandle;

osThreadId TaskReceiveHandle;

osMessageQId myQueueHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartSend(void const * argument);

void StartReceive(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */

  /* definition and creation of myQueue */

  osMessageQDef(myQueue, 16, uint16_t);

  myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

// queue = xQueueCreate(5,12);

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskSend */

  osThreadDef(TaskSend, StartSend, osPriorityNormal, 0, 128);

  TaskSendHandle = osThreadCreate(osThread(TaskSend), NULL);

  /* definition and creation of TaskReceive */

  osThreadDef(TaskReceive, StartReceive, osPriorityNormal, 0, 128);

  TaskReceiveHandle = osThreadCreate(osThread(TaskReceive), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartSend */

/**

  * @brief  Function implementing the TaskSend thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartSend */

void StartSend(void const * argument)

{

  /* USER CODE BEGIN StartSend */

      

       uint16_t buf = 100;

BaseType_t status;

  /* Infinite loop */

  for(;;)

  {

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

       {

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

       {

       status = xQueueSend(myQueueHandle, &buf, 0);

       if (status == pdTRUE)

       printf("写入队列成功,写入值为%d\r\n", buf);

       else

       printf("写入队列失败\r\n");

       }

       while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);

       }

       osDelay(10);

              }

  /* USER CODE END StartSend */

}

/* USER CODE BEGIN Header_StartReceive */

/**

* @brief Function implementing the TaskReceive thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartReceive */

void StartReceive(void const * argument)

{

  /* USER CODE BEGIN StartReceive */

      

       uint16_t buf;

BaseType_t status;

  /* Infinite loop */

  for(;;)

  {

                     if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       status = xQueueReceive(myQueueHandle, &buf, 0);

       if (status == pdTRUE)

       printf("队列数据读取成功,读出值为%d\r\n", buf);

       else

       printf("队列读取失败\r\n");

       }

       while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);

       }

       osDelay(10);

  }

  /* USER CODE END StartReceive */

}

需要说明的是

osMessageQDef(myQueue, 16, uint16_t);

myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

这两个函数是stm32cube封装过的队伍生成函数xQueueCreate(),第一个函数的参数是队伍的名字,队列的长度,一个数的的大小;第二个是返回队伍的句柄,也就是myQueueHandle,我们在xQueueSend和xQueueReceive都要用到该句柄。

我们可以看到当独处的数据超过写入的数据,读出就会失败

freertos.c

再来验证一次性发送多个数据,读多个数据

include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "queue.h"

QueueHandle_t queue;

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

osThreadId TaskSendHandle;

osThreadId TaskReceiveHandle;

osMessageQId myQueueHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartSend(void const * argument);

void StartReceive(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */

  /* definition and creation of myQueue */

  osMessageQDef(myQueue, 5, 12);

  myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

       queue = xQueueCreate(5,12);

  /* USER CODE BEGIN RTOS_QUEES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskSend */

  osThreadDef(TaskSend, StartSend, osPriorityNormal, 0, 128);

  TaskSendHandle = osThreadCreate(osThread(TaskSend), NULL);

  /* definition and creation of TaskReceive */

  osThreadDef(TaskReceive, StartReceive, osPriorityNormal, 0, 128);

  TaskReceiveHandle = osThreadCreate(osThread(TaskReceive), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartSend */

/**

  * @brief  Function implementing the TaskSend thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartSend */

void StartSend(void const * argument)

{

  /* USER CODE BEGIN StartSend */

      

       int buf[3] = {100,200,300};

       BaseType_t status;

  /* Infinite loop */

  for(;;)

  {

             

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

{

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

       {

              status = xQueueSend((QueueHandle_t)queue, buf,500);

              if (status == pdTRUE)

               printf("写入队列成功,写入值为%d,%d,%d\r\n",buf[0],buf[1],buf[2] );

             

              else

               printf("写入队列失败\r\n");

}

while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);

}

osDelay(10);

  }

  /* USER CODE END StartSend */

}

我们可以看到,一次性读写三个数据

一次性读写一个数字与一次性读写三个数字的区别

基于一次性读写一个数字的基础上,我们在开头加入了

#include "queue.h"

QueueHandle_t queue;

在MX_FREERTOS_Init()里面有额外加了queue = xQueueCreate(5,12);

这一段程序的作用就相当于把原来我们在stm32cube里面的对创建任务的参数,又重新配置了一遍。原来的参数是队列长度是16,大小是uint16_t,我们在StartSend和StartReceive传递的参数大小也是uint16_t,所以一次只能传输一个。现在我们把队列的长度改成了5,大小是12字节;我们在StartSend和StartReceive传递的参数大小也是一个int,4个字节,刚好一次性可以传输3个,程序里面细微的才别我就不说了,自己去看看,很容易找的。

所以队列中的每一个数据,一个数据不止只传输一个数字,而是传输一个和你规定队伍参数一样大小的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1319170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

flask简单应用-1

目标: 做一个搜索网页,搜索当前路径下是否含有指定关键字的文件,如果有就列出来,没有返回消息 第一步:我们需要先显示一个搜索页面,页面上需要有一个可以输入的对话框,一个按钮执行搜索 建立ht…

Vue3-05-计算属性使用详解

计算属性简介 计算属性的函数是 computed()。计算属性可以帮助我们处理有复杂逻辑的响应式数据的渲染, 从而代替 模板表达式 的写法。比如 : 一个数值类型的数组对象,我们希望页面展示的只有 偶数。 此时,就可以通过 计算属性 来…

02.Git常用基本操作

一、基本配置 (1)打开Git Bash (2)配置姓名和邮箱 git config --global user.name "Your Name" git config --global user.email "Your email" 因为Git是分布式版本控制工具,所以每个用户都需要…

手拉手EasyExcel极简实现web上传下载(全栈)

环境介绍 技术栈 springbootmybatis-plusmysqleasyexcel 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。 他能让你在不用考虑性…

【PostgreSQL】从零开始:(十三)PostgreSQL-SQL语句操作架构(模式) Schema

Schema概述 PostgreSQL 数据库集群包含一个或多个命名数据库。角色和一些其他对象类型在整个集群中共享。与服务器的客户端连接只能访问单个数据库中的数据,该数据库在连接请求中指定。 用户不一定有权访问集群中的每个数据库。共享角色名称意味着不能在同一集群中…

IDEA2023 + spring cloud 工程热部署设置方法

基于spring cloud 工程进行热部署 &#xff0c;实现每次修改工程源文件&#xff0c;后台自动启动&#xff0c;方便开发测试工作。具体分为5步骤即可&#xff1a; 1、修改工程的pom文件&#xff0c;增加adding devtools 工具包。 <dependency> <groupId>org.s…

1264. 动态求连续区间和(树状数组---某个位置加上一个数/求在线(动态)前缀和/蓝桥杯)

题目&#xff1a; 输入样例&#xff1a; 10 5 1 2 3 4 5 6 7 8 9 10 1 1 5 0 1 3 0 4 8 1 7 5 0 4 8输出样例&#xff1a; 11 30 35 树状数组&#xff1a; 代码&#xff1a; #include<cstdio> #include<iostream> using namespace std;const int N100010; int n,…

【elementui笔记:el-table表格的输入校验】

之前做得比较多的校验是在el-form表单里做的&#xff0c;但有时也遇到&#xff0c;需要在table内输入数据&#xff0c;然后校验输入的数据是否符合要求的情况。因此记录一下。 思路&#xff1a; 1.需要借助el-form的校验&#xff0c;el-table外层嵌套一层el-form&#xff0c;使…

数据分析为何要学统计学(4)——何为置信区间?它有什么作用?

置信区间是统计学中的一个重要工具&#xff0c;是用样本参数()估计出来的总体均值在某置信水平下的范围。通俗一点讲&#xff0c;如果置信度为95%&#xff08;等价于显著水平a0.05&#xff09;&#xff0c;置信区间为[a,b]&#xff0c;这就意味着总体均值落入该区间的概率为95%…

宏基因组学Metagenome-磷循环Pcycle功能基因分析-从分析过程到代码及结果演示-超详细保姆级流程

大背景介绍 生信分析,凡事先看论文,有了论文就有了参考,后续分析就有底了,直接上硬菜开干: PCycDB: a comprehensive and accurate database for fast analysis of phosphorus cycling genes - PubMed 数据库及部分分析代码github库: GitHub - ZengJiaxiong/Phospho…

7.实现任务的rebalance

1.设计 1.1 背景 系统启动后&#xff0c;所有任务都在被执行&#xff0c;如果这时某个节点宕机&#xff0c;那它负责的任务就不能执行了&#xff0c;这对有稳定性要求的任务是不能接受的&#xff0c;所以系统要实现rebalance的功能。 1.2 设计 下面是Job分配与执行的业务点…

深度学习中的潜在空间

1 潜在空间定义 Latent Space 潜在空间&#xff1a;Latent &#xff0c;这个词的语义是“隐藏”的意思。“Latent Space 潜在空间”也可以理解为“隐藏的空间”。Latent Space 这一概念是十分重要的&#xff0c;它在“深度学习”领域中处于核心地位&#xff0c;即它是用来学习…

【每日一题】寻找峰值

文章目录 Tag题目来源解题思路方法一&#xff1a;二分查找 写在最后 Tag 【二分查找】【数组】【2023-12-18】 题目来源 162. 寻找峰值 解题思路 方法一&#xff1a;二分查找 思路 进行二分查找&#xff0c;记当前的二分中点为 mid&#xff1a; 如果 nums[mid] < nums…

UE4 去除重复纹理

如果直接连的话&#xff0c;效果如下&#xff1a; 就存在很多重复的纹理&#xff0c;如何解决这个问题呢&#xff1f; 将同一个纹理&#xff0c;用不同的Tilling&#xff0c;将Noise进行Lerp两者之间&#xff0c;为什么要这么做呢&#xff1f;因为用一个做清晰纹理&#xff0c;…

linux驱动的学习 驱动开发初识

1 设备的概念 在学习驱动和其开发之前&#xff0c;首先要知道所谓驱动&#xff0c;其对象就是设备。 1.1 主设备号&次设备号&#xff1a; 在Linux中&#xff0c;各种设备都以文件的形式存在/dev目录下&#xff0c;称为设备文件。最上层的应用程序可以打开&#xff0c;关…

1852_bash中的find应用扩展

Grey 全部学习内容汇总&#xff1a; https://github.com/GreyZhang/toolbox 1852_bash中的find应用扩展 find这个工具我用了好多年了&#xff0c;但是是不是真的会用呢&#xff1f;其实不然&#xff0c;否则也不会出现这种总结式的笔记。其实&#xff0c;注意部分小细节之后…

[AutoSar]基础部分 RTE 介绍

目录 关键词平台说明一、什么是RTE二、RTE的主要功能 关键词 嵌入式、C语言、autosar、EcuM、wakeup、flex 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 一、什么是RTE RTE&#xff08;Run-Time Environment&…

Docker 的基本概念、优势、及在程序开发中的应用

Docker 是一种容器化平台,它通过使用容器化技术,将应用程序及其依赖性打包到一个独立的、可移植的容器中,从而实现应用程序的快速部署、可靠性和可扩展性。 下面是 Docker 的一些基本概念和优势: 容器:Docker 使用容器化技术,将应用程序及其依赖性打包到一个可移植的容器…

网络基础(十一):VRRP原理与配置

目录 前言&#xff1a; 1、VRRP的基本概述 2、VRRP的基本原理 2.1VRRP的基本结构 2.2设备类型 2.3状态机 2.4VRRP路由器的抢占功能 2.5VRRP路由器的优先级 2.6VRRP工作原理 2.7主备路由器的工作内容 3、VRRP的基本配置 3.1配置主路由器和备用路由器 3.2配置PC1与P…

DOM树和DOM对象与JS关系的深入研究

const和let使用说明 var不好用&#xff0c;我们如果用变量都是用let&#xff0c;如果用常量乃是不变的量&#xff0c;我们用const&#xff0c;见let const知变量是否可变。比如一个常量在整个程序不会变&#xff0c;但是你用let&#xff0c;是可以的。但是let最好与内部变量改…