一文搞定国民N32G435高负载串口通信

news2024/7/6 19:43:20

副标题:USRAT无硬件双缓冲条件下的软件双缓冲

一、前言

在单片机中,USART的通信一般都是最常用也最先去接触的串口外设,在一般的小数据量应用中一般不需要考虑USART串口(以下简称为串口)的高负载能力,比如打印一下log,接收几个其他设备的指令或者发送几个指令控制其他设备。但是在高速的大数据量的通信场合,串口可能会承载较高的数据负载,如果不合理的进行单片机的资源利用,可能造成各种问题。比如使用串口接收中断接收大量的数据,频繁的进入中断,会占用太多的CPU资源。这时可能会想到【空闲中断+DMA传输完成中断】的方式接收大量数据,但是这是一个极具风险的行为,假设一下,DMA数据传输结束之后,此时CPU开始读取DMA缓存中的数据,此时又有新的数据进来,新的数据就会覆盖之前的数据导致异常。

二、如何启用串口的DMA功能

在讨论如何高实现串口的高负载通信之前,我们得先明白如何启用串口的DMA通信。

DMA(Direct Memory Access)直接储存器访问,是一个CPU用于数据从一个地址空间到另一个地址空间的搬运组件,该过程无需CPU的干预,不占用CPU的资源,可以使单片机这种单线程CPU实现“伪多线程”。只需在数据搬运结束后通知CPU即可。

在国民的资料中是有串口+DMA的例程的,但是官方为了用户调试方便,例程相对简单,就是实现了两个MCU串口间的DMA通信,在开发时具有一定借鉴意义,但是不具备高负载能力,同时移植性不是很好,这里我在例程的基础上进行简化,同时例程不具备的功能也会一一展开。

1.串口+DMA发送

#define TxBufferSize1 (countof(TxBuffer1) - 1)
#define countof(a) (sizeof(a) / sizeof(*(a)))
USART_InitType USART_InitStructure;
uint8_t TxBuffer1[20] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a};

首先是定义一些相关的变量,数据和结构体啥的,TxBufferSize1 发送数量,TxBuffer1[20]发送的数组。

/**
* [url=home.php?mod=space&uid=247401]@brief[/url]  Configures the different system clocks.
*/
void RCC_Configuration(void)
{
    /* DMA clock enable */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);
    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
    /* Enable USARTy and USARTz Clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, ENABLE);
  
}

/**
* [url=home.php?mod=space&uid=247401]@brief[/url]  Configures the different GPIO ports.
*/
void GPIO_Configuration(void)
{
    GPIO_InitType GPIO_InitStructure;

    /* Initialize GPIO_InitStructure */
    GPIO_InitStruct(&GPIO_InitStructure);

    /* Configure USARTy Tx as alternate function push-pull */
    GPIO_InitStructure.Pin            = GPIO_PIN_6;   
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF0_USART1;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Configure USARTy Rx as alternate function push-pull and pull-up */
    GPIO_InitStructure.Pin            = GPIO_PIN_7;
    GPIO_InitStructure.GPIO_Pull      = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF0_USART1;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);   

}

对相关的时钟和串口的引脚进行初始化,这里是直接用的官方例程,只不过将官方例程的宏定义换成了实际的值,便于看代码,不然还需跳转,但是官方的例程这方面的可移植性会更好。

void DMA_Configuration(void)
{
    DMA_InitType DMA_InitStructure;

    /* USARTy TX DMA1 Channel (triggered by USARTy Tx event) Config */
    DMA_DeInit(DMA_CH4);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = (USART1_BASE + 0x04);
    DMA_InitStructure.MemAddr        = (uint32_t)TxBuffer1;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = TxBufferSize1;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(DMA_CH4, &DMA_InitStructure);
    DMA_RequestRemap(DMA_REMAP_USART1_TX, DMA, DMA_CH4, ENABLE);

}

DMA的初始化采用NORMAL模式,即只发送一次,当计数器为0时便不再搬运数据。

void UART_Init(USART_Module* USARTx,uint32_t BaudRate)
{
    /* USARTy and USARTz configuration ------------------------------------------------------*/
    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate            = BaudRate;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure USARTy and USARTz */
    USART_Init(USARTx, &USART_InitStructure);


    /* Enable USARTy DMA Rx and TX request */
    USART_EnableDMA(USARTx, USART_DMAREQ_RX | USART_DMAREQ_TX, ENABLE);

    /* Enable the USARTy and USARTz */
    USART_Enable(USARTx, ENABLE);

}

串口的初始化。

void DMA_send(uint8_t* pBuffer,uint16_t BufferLength)
{
        DMA_EnableChannel(DMA_CH4, DISABLE);
        DMA_SetCurrDataCounter(DMA_CH4,BufferLength);
        DMA_EnableChannel(DMA_CH4, ENABLE);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXDE) == RESET)
    {
               
    }
}

DMA的发送函数,先失能DMA通道,再重新设置传输长度,再使能DMA通道,这里是检测while是检测串口的发送完成编制位,在官方的demo中检测的是DMA的通道完成标志,这个在这里面是不可以的,因为DMA的搬运速度是远大于串口的通信速度的,如果检测DMA通道完成标志,会导致DMA已经将数据搬运到串口的数据寄存器,但是因为串口的速度不够,导致此时数据还未送出,而因为例程只循环一次,在测试例程时看不出问题,但是这里会出问题。

int main(void)
{
    /* System Clocks Configuration */
    RCC_Configuration();

    /* Configure the GPIO ports */
    GPIO_Configuration();

    /* Configure the DMA */
    DMA_Configuration();

        UART_Init(USART1,115200);

    while (1)
    {
                DMA_send(TxBuffer1,20);
                Delay(10000000);
    }
}

最后在主函数调用各初始化函数,在while (1)中循环发送便可实现最简单的串口+DMA发送。
在这里插入图片描述

2.串口+DMA接收

在上面发送的基础上我们加上DMA的接收功能,此处需要解释一下下面的操作:为了对应书册,上面的串口发送DMA通道原来是CH4,我下面全部改成CH1。

uint8_t RxBuffer1[20];

定义一个数组用于接收串口数据。

USART_ConfigInt(USARTx, USART_INT_IDLEF, ENABLE);

添加串口中断定义。

void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;

/* Enable the USARTz Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

添加NVIC配置。

void DMA_Configuration(void)
{
DMA_InitType DMA_InitStructure;

/* USARTy TX DMA1 Channel (triggered by USARTy Tx event) Config */
DMA_DeInit(DMA_CH1);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (USART1_BASE + 0x04);
DMA_InitStructure.MemAddr = (uint32_t)TxBuffer1;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStructure.BufSize = TxBufferSize1;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
DMA_Init(DMA_CH1, &DMA_InitStructure);
DMA_RequestRemap(DMA_REMAP_USART1_TX, DMA, DMA_CH1, ENABLE);

DMA_DeInit(DMA_CH2);
DMA_InitStructure.PeriphAddr = (USART1_BASE + 0x04);
DMA_InitStructure.MemAddr = (uint32_t)RxBuffer1;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = TxBufferSize1;
DMA_Init(DMA_CH2, &DMA_InitStructure);
DMA_RequestRemap(DMA_REMAP_USART1_RX, DMA, DMA_CH2, ENABLE);
}

添加DMA的接收,并将通道设置为CH2。

void DMA_Revice(uint16_t BufferLength)
{
DMA_EnableChannel(DMA_CH2, DISABLE);
DMA_SetCurrDataCounter(DMA_CH2,BufferLength);
DMA_EnableChannel(DMA_CH2, ENABLE);

}

添加DMA接收函数

void USART1_IRQHandler(void)
{
if (USART_GetIntStatus(USART1, USART_INT_IDLEF) != RESET)
{
/*软件先读 USART_STS,再读 USART_DAT 清除空闲中断标志。*/
USART1->STS;
USART1->DAT;
for(int i=0;i<20;i++)
{
TxBuffer1[i] = RxBuffer1[i];

}
DMA_send(20);
DMA_Revice(20);

}
}

添加串口中断函数,在串口中断函数中将接收的数据传给DMA发送数组,再通过DMA的方式发送出来用于校验结果。
在这里插入图片描述
通过串口助手可观测数据正确。至此,常见的串口+DMA的发送与接收完成。后文将实现高负载的通信。

三、高负载情况下的DMA如何实现

在串口数据量较大时,一般使用双BUf,很多单片机有硬件双缓冲,DMA的目标储存区域有两个,当一次完整的数据传输结束后,也就是counter值变为0时,DMA会自动将数据指向另一块区域。这样用户就有时间去处理刚存满的buf,而不会被覆盖。就是“乒乓缓存”。
普通DMA
在这里插入图片描述
DMA双缓冲
在这里插入图片描述
大致流程如下:

1.串口有数据到来,DMA现将数据储存在内存1,完成后通知CPU过来处理数据。
2.此时DMA不停下,开始将后续的数据搬运到内存2。
3.内存2的数据搬运完成,通知CPU开始处理内存2中的数据。
4.如果数据传输还未结束,此时DMA会将数据储存在内存1。如此循环,直至没有数据到来。

但是遗憾的是N32G435这块芯片不具备双缓冲模式,那么我们可以主动控制DMA跳转内存区域。利用“传输过半中断”来模拟双缓冲模式。

大致流程如下:

1.DMA完成搬运一半的数据时,产生一个传输过半中断,此时我们让CPU来处理上一半数据。
2.DMA数据搬运未停止,此时继续搬运后一半数据,此操作不会影响前面一半的数据处理。
3.DMA数据搬运完,触发传输完成中断,这时CPU可以处理后半数据。
4.如果数据传输还未结束,DMA继续将数据向前半搬运,如此循环。

在这里插入图片描述
代码讲解如下:

以下代码完整流程如下:

1.配置串口波特率2.5M,DMA的BufSize设置为40,开启传输过半中断,传输完成中断,串口空闲中断。
2.启动DMA接收。
3.通过串口助手发送80个数据到串口。
4.当DMA接收数组接收到20个数据触发传输过半中断,跳转中断函数将20个数据存放到数组中。
5.此时DMA仍在运行,但是数据存放在DMA接收数组的后20个地址空间。
6.当DMA接收数组填满,触发DMA传输完成中断,跳转中断函数将后20个数据保存,此时DMA一共搬运了40个数据。
7.DMA继续搬运数据到接收数组里,此时会覆盖之前的前二十个数据,跳转到步骤4.
8.接收完80个数据,此时触发串口空闲中断,将接收到的数据打印出来。

在上面代码基础上做如下操作:

1.将DMA CH2通道设置为循环模式,测试阶段将BufSize设置为40,开启传输过半中断和传输完成中断。同时为了测试高速场景,串口波特率设置为2.5M:

    DMA_DeInit(DMA_CH2);
DMA_InitStructure.PeriphAddr = (USART1_BASE + 0x04);
DMA_InitStructure.MemAddr = (uint32_t)buffer;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = 40;
DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR;
DMA_Init(DMA_CH2, &DMA_InitStructure);
DMA_RequestRemap(DMA_REMAP_USART1_RX, DMA, DMA_CH2, ENABLE);

DMA_ConfigInt(DMA_CH2,DMA_INT_HTX,ENABLE);//半传输中断
DMA_ConfigInt(DMA_CH2,DMA_INT_TXC,ENABLE);//传输完成中断
DMA_ClearFlag(DMA_FLAG_HT2,DMA);//清除标志位,避免第一次传输出错
DMA_ClearFlag(DMA_FLAG_TC2,DMA);
DMA_ClrIntPendingBit(DMA_INT_HTX2,DMA);
DMA_ClrIntPendingBit(DMA_INT_TXC2,DMA);
UART_Init(USART1,2500000);

2.NVIC设置DMA通道中断

void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;

/* Enable the USARTz Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = DMA_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

}

3.添加DMA的CH2中断函数,num为全局变量,目的是将所有的数据保存进buf数组:

void DMA_Channel2_IRQHandler(void)
{
//传输半满
if(DMA_GetIntStatus(DMA_INT_HTX2,DMA) == SET)
{
DMA_ClrIntPendingBit(DMA_INT_HTX2,DMA);
DMA_ClearFlag(DMA_FLAG_HT2,DMA);
for(int i=0;i<20;i++)
{
buf[num] = buffer[i];
num++;
}
}
//传输满
if(DMA_GetIntStatus(DMA_INT_TXC2,DMA) == SET)
{
DMA_ClrIntPendingBit(DMA_INT_TXC2,DMA);
DMA_ClearFlag(DMA_FLAG_TC2,DMA);
for(int i=20;i<40;i++)
{
buf[num] = buffer[i];
num++;
}
}
}

4.在串口空闲中断中将收到的数据全部打印出来。

void USART1_IRQHandler(void)
{
if (USART_GetIntStatus(USART1, USART_INT_IDLEF) != RESET)
{
/*软件先读 USART_STS,再读 USART_DAT 清除空闲中断标志。*/
USART1->STS;
USART1->DAT;
for(int i=0;i<80;i++)
{
TxBuffer1[i] = buf[i];

}
DMA_send(80);
num=0;
}
}

5.测试结果如下,在2.5M波特率的情况下保持数据完整。

在这里插入图片描述

写在最后:

这次主要讨论了一种高负载情况下如何缓解CPU压力的方法,所言所写不尽完善,例如不定数据接收,就可以通过DMA_GetCurrDataCounter(DMA_CH2);函数进行传输数据的统计计算,这点大家可以自由发挥,现实可能遇到的问题是多种多样的,主要在于关键能力的拓展。更多的还需要根据实际情况灵活配置。

源码链接

N32G435串口通信

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

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

相关文章

第六十五天学习记录:高等数学:函数与极限(宋浩板书)

C语言学习后&#xff0c;曾为先学C还是数据结构纠结了半天。在看数据结构前言的时候&#xff0c;发现学习数据结构之前还需要一定的数学基础。虽然涉及到的数学基础不多&#xff0c;但想到以前大学高数&#xff0c;现代不是60分万岁就是不到80分&#xff0c;好像就概率论稍微了…

【Python】Python系列教程-- Python3 循环语句(十七)

文章目录 前言while 循环无限循环while 循环使用 else 语句简单语句组for 语句for...elserange() 函数break 和 continue 语句及循环中的 else 子句pass 语句 前言 往期回顾&#xff1a; Python系列教程–Python3介绍&#xff08;一&#xff09;Python系列教程–Python3 环境…

Treap C++代码实现

一、全部代码 #include <iostream> #include <cstdlib> #include <time.h>using namespace std;//Treap结构 struct TreapNode {TreapNode *pLeft, *pRight;int value, fix; };//左旋 void TreapLeftRotate(TreapNode* &pCur){//首先&#xff0c;找到当…

平衡树原理讲解

平衡树——Treap 文章目录 平衡树——TreapBST定义性质操作插入insert(o, v)删除del(o, v)找前驱 / 后继get_prev(o)、get_next(o)查找最大 / 最小值get_min(o)、get_max(o)求元素排名get_rank(o)查找排名为 k k k的元素get_value_by_rank 平衡树左旋、右旋zag(o)、zig(o)左旋右…

Vue路由(vue-router)详细讲解指南

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成&#xff0c;让构建单页面应用变得易如反掌。路由实际上就是可以理解为指向&#xff0c;就是我在页面上点击一个按钮需要跳转到对应的页面&#xff0c;这就是路由跳转&#xff1b; 首先我们来学习三个单词&…

运维小白必学篇之基础篇第八集:LVM实验

LVM实验 实验一&#xff1a; 使用/dev/sdb磁盘完成以下操作&#xff1a; 1、创建5个物理卷&#xff0c;组成大小为10G的卷组vgtest 2、创建大小为3G的逻辑卷lv1&#xff0c;格式化xfs文件系统&#xff0c;实现开机自动挂载 3、为lv1逻辑卷扩容至5G&#xff0c;然后查看扩容之…

男子路遇“纸片鸟”,AI帮忙免惹祸

据报道&#xff0c;近日&#xff0c;河南洛阳一网友在路边偶遇一只“纸片鸟”&#xff0c;小鸟远看像一张纸片&#xff0c;样子十分奇特&#xff0c;而且还死死地盯着自己&#xff0c;像是求救&#xff0c;后来他用手机一查发现是二级保护动物“黄斑苇鳽”&#xff0c;便报警处…

排水管网监测预警系统:为城市排水建设提质增效

最近&#xff0c;城市生命线安全工程占据着行业的头条榜单&#xff0c;与民众生活密不可分的城市基础设施&#xff0c;包括城市的燃气、桥梁、供水、排水、供热、综合管廊等被称为城市生命线。城市生命线安全工程是城市更新和新型城市基础设施建设的重要内容&#xff0c;其主要…

数说故事×中广协丨广告代言人内容商业沙龙走进大湾区

以“娱时俱进&#xff0c;内容赋能”为主题的广告代言人内容商业沙龙&#xff08;大湾区站&#xff09;&#xff0c;近日在广州市白云区美湾广场成功举行。 沙龙由中国广告协会作为指导单位&#xff0c;广东省广告协会与中国广告协会广告代言人工作委员会联合主办&#xff0c;广…

机器视觉陶瓷板外观缺陷检测系统应用

随着科技的不断发展&#xff0c;机器视觉技术在工业领域的应用越来越广泛。其中&#xff0c;机器视觉陶瓷板外观缺陷检测系统是一项十分重要的技术。该系统利用计算机视觉技术对陶瓷板表面的缺陷进行自动化检测&#xff0c;大大提高了生产效率和产品质量。 机器视觉陶瓷板外观…

智能应用搭建平台——LCHub低代码表单 vs 流程表单 vs 仪表盘

1. LCHub低代码如何选择 「流程表单」:填报数据,并带有流程审批功能,适合报销、请假申请或其他工作流; 「表单」:填报数据,并带有数据协作功能,如修改、删除、导入、导出,并可以给不同的人不同的管理权限; 「仪表盘」:数据分析处理、结果展示功能,如数据汇总、趋…

如何实现Web3去中心化云计算的大规模采用?

随着区块链技术的迅猛发展&#xff0c;Web3去中心化云计算正在逐渐崭露头角。它以分布式、安全和透明的特点&#xff0c;为用户和企业提供了许多独特的优势。 然而&#xff0c;要实现Web3去中心化云计算的大规模采用&#xff0c;仍然面临着一些挑战。本文将探讨这些挑战&#x…

[EuroSys2023 Best Poster] 面向动态图的极低时延GNN推理采样服务

作者&#xff1a;沈雯婷 GraphLearn是阿里云机器学习平台PAI 团队和达摩院智能计算实验室图计算团队共建的工业界大规模图神经网络训练框架, 也是一站式图计算平台GraphScope的图学习引擎。GraphLearn最新开源了面向动态图的GNN在线推理实时采样服务(DGS)。DGS具备处理实时高吞…

端口占用解决报错:Web server failed to start. Port 8080 was already in use.

报错信息如下: Description: Web server failed to start.Port 8080 was already in use. Action: Identify and stop the process thats listening on port 8080 or configure this application to listen on another port. 当idea项目启动时报这个错&#xff0c;意思是端…

2019年一月联考逻辑真题

2019年一月联考逻辑真题 三、逻辑推理&#xff1a;第 26&#xff5e;55 小题&#xff0c;每小题 2 分&#xff0c;共 60 分。下列每题给出的 A、B、C、D、 E 五个选项中&#xff0c;只有一项是符合试题要求的。请在答题卡上将所选项的字母涂黑。 真题&#xff08;2019-26&#…

【数据结构】虽然很难很抽象,但是你还是得努力弄懂的数据结构——链表,基本上你每一段代码都可能会用到

链表解决了顺序表插入或删除元素麻烦的问题&#xff0c;链表的存储结构是用一组任意的存储单元来存放线性表的数据元素&#xff0c;这组存储单元可以是连续的&#xff0c;也可以是不连续的。 对每个数据元素ai&#xff0c;除了存储其本身的信息之外&#xff0c;还需存储一个指…

【Vue】详解Vue生命周期

Vue实例的生命周期全过程&#xff08;图&#xff09; &#xff08;这里的红边圆角矩形内的都是对应的Vue实例的钩子函数&#xff09; 在beforeCreate和created钩子函数间的生命周期 在beforeCreate和created之间&#xff0c;进行数据观测(data observer) &#xff0c;也就是在这…

港联证券投资前瞻:新能源汽车再迎助力 科技巨头持续加注机器人领域

上周五&#xff0c;A股主要股指全线走高&#xff0c;沪指涨近1%&#xff0c;上证50指数涨近2%。截至收盘&#xff0c;沪指涨0.79%报3230.07点&#xff0c;深成指涨1.5%报10998.08点&#xff0c;创业板指涨1.22%报2233.27点&#xff0c;上证50指数涨1.73%&#xff1b;两市合计成…

圆梦,终于进阿里了,分享面试题

前面我说过&#xff1a;能去大厂就去大厂&#xff0c;有机会就去争取&#xff0c;年纪轻轻的&#xff0c;多努力就完事了。 总有黑粉怼我&#xff1a;进大厂哪有你说的那么简单&#xff0c;呵呵…… 我笑而不语&#xff0c;你自己都不相信自己&#xff0c;还怎么进&#xff1…

在 Python 中为对象添加属性

我们将介绍如何在 Python 中为对象添加属性。 我们还将通过示例介绍如何在 Python 中更改对象的属性。 在 Python 中为对象添加属性 在 Python 中&#xff0c;我们时常使用对象&#xff0c;因为 Python 是一种面向对象的语言。 对象使我们的代码可重用并易于实现复杂的结构。 …