STM32F103之DMA

news2024/11/17 1:34:11

DMA简介

DMA,即Direct Memory Access,是一种在无需CPU参与的情况下,将数据在存储器(单片机的RAM)和外设(一般是I/O设备)之间高效传输的硬件机制。实现这种功能的集成电路单元叫做DMA Controller,即DMA 控制器。

DMA的优势

一般情况下,为了实现单片机的RAM和外设之间的数据传输,有三种常用的方法:轮循法(polling),中断法(interrupt)以及本文要介绍的DMA方法。

轮循法(polling):在主循环中,CPU不断检查外设的相关标志位,来判断其是否需要进行数据的传输,如果有,则CPU将数据在外设和内存之间搬运,实现数据传输。当数据传输服务请求频繁或者的数据量很大时,会影响其他任务的实时性。由于其他任务的存在,也会影响数据传输的实时性。

中断法(interrupt):当外设需要传输数据时,会触发中断,CPU会暂停正在处理的任务,转而去处理外设的数据传输任务。CPU无需反复检查外设的标志位,中断机制会指示CPU何时去处理外设数据,但是依然需要CPU去完成数据搬运和传输过程。当外设数据传输服务发生不频繁,且数据量不大时,中断法也是不错的选择。当中断连续不断且频繁发生时,中断法变得不再高效,因为在恢复主流程的执行和中断响应的上下文切换上会占用大量CPU时间。

DMA传输法:DMA控制器是一种单片机中的硬件单元,他的功能就是允许I/O外设和存储器之间高效传输数据,且传输过程中无需CPU参与。

DMA的工作原理

  1. 当一个IO设备需要发送数据给存储器或者从存储器读取数据时,它会给DMA 控制器发送一个DRQ请求。DMA控制器收到请求后,向CPU发送HLD请求,要求CPU放弃对总线的使用,因为DMA控制器传输数据时,要使用系统总线。
  2. CPU收到DMA控制器的HLD请求后,让出总线使用权给DMA控制器,并向DMA控制器响应HLDA信号。
  3. 当DMA控制器收到CPU的HLDA信号后,DMA控制器会通知IO设备一个DACK信号,告知IO设备可以进行数据的传输。然后DMA控制器占用系统总线,完成IO设备和存储器之间的数据传输。
  4. 当数据传输完成后,DMA控制器向CPU申请中断,告知CPU数据传输完成。同时CPU恢复系统总线的使用权。

从上描述可以发现:

  • 在数据传输过程中,CPU完全不需要参与,只有传输完成后才有必要参与进来,大大的节约了CPU时间。
  • DMA数据传输请求总是IO外设发起的。

STM32F103的DMA

STM32F103系列单片机最多拥有2个DMA控制器DMA1和DMA2。其中DMA1有7个通道,DMA2有5个通道。注意DMA2仅在大容量和互联型型号中才存在,例如STM32F103x8, STM32F103xB只有DAM1,而STM32F103xC, STM32F103xD, STM32F103xE有DMA1和DMA2

每个DMA控制器下有多个通道,DMA通道是实现外设与存储器之间数据传输的管道。通道之间的是相互独立工作的。所以在具体实现某个外设与存储器之间DMA传输时,就需要围绕通道展开,配置相关通道的寄存器。

每个DMA通道可允许多个外设与存储器之间进行数据传输,但任意时刻只能选择其中一个来进行。详见《参考手册》的DMA请求映像。如下图所示可以发现,STM32F103的DMA1的通道1允许ADC1、TIM2_CH3或者TIM4_CH1与存储器之间进行数据传输。

通道数据传输方向

通道的数据传输方向主要有两种:

  • 存储器到外设:例如USART使用DMA方式发送数据,待发送的数据可能是字符串或者一个字节数组。当使用DMA发送时,这些数据从存储器按字节为单位依次传输到USART的发送缓冲寄存器TDR进行发送。
  • 外设到存储器:例如USART使用DMA方式接收数据,会使用一个字节数组来缓存收到的数据。当使用DMA接收时,收到的数据从USART的接收缓冲寄存器RDR依次传输到存储器进行缓存。

通道传输方向通过DMA_CCRx寄存器的DIR位进行选择:

  • DMA_CCRx.DIR=0:外设为数据源,方向为外设到存储器
  • DMA_CCRx.DIR=1:存储器为数据源,方向为存储器到外设

使用标准外设库进行配置时,通过如下代码进行设置:

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //外设是数据目的地,存储器->外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设是数据源,外设->存储器

通道外设和存储器的数据位宽

DMA通道传输数据的时候,每次传输需要从外设读出数据写入到存储器,或者相反从存储器读出数据写入到外设,那么每次读/写的单个数据占多少字节大小呢?这分别通过设置【外设数据宽度】和【存储器的数据宽度】来指定。

如果【外设数据宽度】和【存储器的数据宽度】是一样的,则很好理解,无需详细讨论。但如果不一样,则在传输到目的地时会发生高位补0或者截断高位的情况。如下表所示。

数据源 --> 数据目的地
数据源宽度(位)源数据数据目的地宽度(位)目的地数据
8

0xB1

160x00B1
80xB1320x000000B1
160xB1B280xB2
320xB1B2B3B480xB4

如果我们把地址自增的情况也考虑进来,就会发现数据宽度也影响了地址自增时,地址值的增量大小。以存储器为例,地址模式设置为自增模式。

  • 如果数据宽度为 8位,则每次读/写一个字节后,地址值增加1。
  • 如果数据宽度为16位,则每次读/写两个字节后,地址值增加2。
  • 如果数据宽度为32位,则每次读/写四个字节后,地址值增加4。

外设数据宽度通过DMA_CCRx.PSIZE设置:

  • DMA_CCRx.PSIZE=00:8位
  • DMA_CCRx.PSIZE=01:16位
  • DMA_CCRx.PSIZE=10:32位
  • DMA_CCRx.PSIZE=11:保留

存储器数据宽度通过DMA_CCRx.MSIZE设置:

  • DMA_CCRx.MSIZE=00:8位
  • DMA_CCRx.MSIZE=01:16位
  • DMA_CCRx.MSIZE=10:32位
  • DMA_CCRx.MSIZE=11:保留

使用标准外设库进行配置时,通过如下代码进行设置:

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //外设数据宽度8位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //16
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;  //32位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  //存储器数据宽度8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;  //32位

通道外设/存储器地址与地址生成算法

在进行传输前,需要为通道设置好外设基地址和存储器基地址,同时也要设置外设地址和存储器地址在每次传输一个数据时应该怎样变化。这样DMA才知道每次应该从哪个地址去取数据并传输到哪里。

例如,我们使用DMA实现USART发送数据,待发送的数据缓存在字节数组DMA_txbuff中,USART的发送缓冲寄存器为USART_TDR,则存储器基地址为DMA_txbuff数组在内存中的地址,外设地址为USART_TDR寄存器的地址。USART通过DMA发送一个字节时传输时,第一次会从DMA_txbuff [0] 取出一个字节传输到USART_TDR,然后下一次会从DMA_txbuff [1]取出一个字节传输到USART_TDR,因此可以发现,传输时存储器地址是自增的,而外设地址则是固定的。所以这就是地址生成算法的两种选择:地址递增模式和地址固定模式。

一般情况下,我们会设置DMA通道的外设地址是固定的,而存储器地址是递增变化的。

请注意,即便是在地址自动递增模式下,DMA_CPARx/DMA_CMARx寄存器的值都不会随着传输的进行而自动改变,除非代码重新给他们赋值。DMA通道通过内部的隐藏的地址游标寄存器进行递增变化来实现地址递增模式的,为了方便描述本文把这种隐藏寄存器叫做地址游标寄存器。也就是说实际传输时DMA是通过地址游标寄存器去找数据的,DMA_CPARx/DMA_CMARx寄存器都只是记录一个基地址而已。

例如对于存储器的地址递增模式,每次给DMA_CMARx寄存器进行赋值时,内部隐藏的地址游标寄存器DMA_CMARx_cursor也会被赋值。假设初始化 DMA_CMARx=0x01,则DMA_CMARx_cursor=0x01,传输一个字节后,DMA_CMARx依然为0x01,而DMA_CMARx_cursor=0x02。如此下去。

外设与存储器基地址设置分别通过DMA_CPARx寄存器和DMA_CMARx寄存器设置:

  • DMA_CPARx = (uint32_t) &USART_TDR ;
  • DMA_CMARx = (uint32_t) &DMA_txbuff ;

如果使用标准外设库,则代码:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);  //外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_DMA_rxbuf;  //存储器基地址

外设与存储器地址生成算法的选择分别通过PINC与MINC设置,如下:

  • DMA_CCRx.MINC=0:存储器地址递增模式
  • DMA_CCRx.MINC=1:存储器地址固定模式
  • DMA_CCRx.PINC=0:外设地址递增模式
  • DMA_CCRx.PINC=1:外设地址固定模式

如果使用标准外设库,则代码:

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;     //存储器地址递增

通道数据传输长度

通道在执行一次传输任务时,必须在初始化时指定传输长度,即本次传输需要传输多少个基本数据。

DMA_CNDTRx寄存器用于设置DMA通道的初始传输长度,在DMA传输过程中,也用于表示还剩多少数据没有完成传输。只有在通道关闭的情况下DMA_CNDTRx寄存器才能改写,一旦通道使能,DMA_CNDTRx寄存器为只读的,并在每个 DMA 传输之后值减 1。如果该寄存器的值为 0,无论通道开启与否,都不会有数据传输。

DMA_CNDTRx寄存器最大值为65535,因此通道的单次传输长度最大为65535个数据。

在使用标准库时,通过如下代码操作DMA_CNDTRx寄存器:

//在通道配置时初始化DMA_CNDTRx寄存器,设置传输长度
DMA_InitStructure.DMA_BufferSize = 255;

//设置通道的DMA_CNDTRx寄存器,设置传输长度
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
//读取通道的DMA_CNDTRx寄存器,读取剩余传输长度
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

通道的传输模式

通道有两种传输模式,一种是在完成指定的传输长度后就停止,另一种是每次完成初始指定的传输长度时又立刻恢复到初始状态继续提供DMA服务。这就是非循环模式和循环模式。

当通道配置为非循环模式时,每传输一次,DMA_CNDTRx寄存器就减少1,当传输结束,即DMA_CNDTRx寄存器变为0时,无法再接受外设的DMA传输请求。要开始新的DMA传输,需要在关闭DMA通道的情况下,向DMA_CNDTRx寄存器中重新写入新的传输长度,并再次开启DMA通道才能进行新的传输任务。

而在循环模式下,每轮传输结束时,即当DMA_CNDTRx寄存器为0的瞬间,DMA_CNDTRx寄存器的值会立刻自动加载为其初始数值(最近一次被赋值的值),因此DMA通道也就一直处于提供传输服务状态。另一方面,内部隐藏的地址游标寄存器也都会被重新加载,即设置 DMA_CPARx_cursor  = DMA_CPARx ,DMA_CMARx_cursor = DMA_CMARx。简而言之就是自动重新开始一次初始化状态下的新的传输

那么问题来了,循环模式下,DMA_CNDTRx寄存器是怎么知道其初始值的呢?这里需要补充说明:DMA_CNDTRx寄存器带有有一个隐藏的备份寄存器,暂且叫他DMA_CNDTRx_backup,在每次DMA_CNDTRx寄存器被赋值时,DMA_CNDTRx_backup会保存其初始值,当DMA_CNDTRx寄存器为0的瞬间,就会有DMA_CNDTRx = DMA_CNDTRx_backup。

通过DMA_CCRx.CIRC位来选择循环模式和非循环模式:

  • DMA_CCRx.CIRC=0:非循环模式
  • DMA_CCRx.CIRC=1:循环模式

在标准库开发时,通过代码:

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //循环模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;    //非循环模式

通道软件优先级

同一个DMA控制器下可能会启动多个DMA通道,当同时有多个通道需要执行DMA传输请求时,就需要仲裁哪一个优先执行。这便是通过软件优先级来实现的。

当通道软件优先级不同时,以软件优先级来决定哪个通道的优先传输,软件优先级高的先被响应。当软件优先级相同时,那就看硬件优先级,通道号越低的硬件优先级越高,例如通道0硬件优先级高于通道1。硬件优先级详见《参考手册》DAM请求映像。

通道的优先级在DMA_CCRx.PL中设置,有4个等级:

  • DMA_CCRx.PL=11:最高优先级
  • DMA_CCRx.PL=10:高优先级
  • DMA_CCRx.PL=01:中等优先级
  • DMA_CCRx.PL=00:低优先级

使用标准外设库进行配置时代码如下:

DMA_InitStructure.DMA_Priority = DMA_Priority_High;  //设置通道优先级

通道的启用和禁用

一旦启动了DMA通道,它就可响应连到该通道上的外设的DMA请求。

DMA_CCRx.EN位用于使能DMA通道。标准外设库代码如下:

DMA_Cmd(DMA1_Channel5, ENABLE);   //开启DMA通道
DMA_Cmd(DMA1_Channel5, DISABLE);  //关闭DMA通道

DMA中断

DMA传输过程中可以触发三种中断:

  • 传输完成中断(TC):随着传输进行,当DMA_CHxCNT递减为0时,触发传输完成中断。循环模式时每轮DMA_CHxCNT递减为0也会触发。
  • 传输半完成中断(HT):假设设置的传输长度DMA_CHxCNT的值为N,则当传输完成一半(整数除法,N/2,例如7/2=3)后,触发传输半完成中断。
  • 传输错误中断(TE):传输过程中发生错误时触发此中断。

通道也有一个全局中断标志GIF,GIF没有对应的中断使能,所以它不能独立触发中断。当TCIF、HTIF、TEIF任意中断标志置位时,GIF硬件置位。当清除GIF标志时,也会一并清除TCIF、HTIF、TEIF。

寄存器DMA_IFCR用于软件清除中断标志,写0无效,写1清除对应标志。

//使能或者禁用指定通道的某个中断:TC、HT、TE
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, 
                  uint32_t DMA_IT, 
                  FunctionalState NewState);

//读取通道指定的中断是否处于挂起状态:TC、HT、TE、GL
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);

//清除通道的中断标志:TC、HT、TE、GL
void DMA_ClearITPendingBit(uint32_t DMAy_IT);

DMA代码示例

//使能DMA1外设时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  


//DMA相关宏定义
#define USART1_RX_DMA_CH       DMA1_Channel5   
#define USART1_TX_DMA_CH       DMA1_Channel4
#define USART1_DMA_TXBUF_SIZE  255
#define USART1_DMA_RXBUF_SIZE  255
uint8_t USART1_DMA_txbuf[USART1_DMA_TXBUF_SIZE];
uint8_t USART1_DMA_rxbuf[USART1_DMA_RXBUF_SIZE];



//======USART1 RX的DMA通道配置========
DMA_DeInit(USART1_RX_DMA_CH);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART1->DR));    //外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_DMA_rxbuf;       //存储器基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                       //传输方向:外设是数据源,外设->存储器
DMA_InitStructure.DMA_BufferSize = USART1_DMA_RXBUF_SIZE;                //传输长度:DMA_CNDTRx寄存器的值
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;         //外设地址生成算法,外设地址不增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                  //存储器地址生成算法,存储器地址增加
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //外设寄存器单个数据宽度:Byte代表8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;          //存储器单个数据宽度:Byte代表8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                          //循环传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;                      //通道的软件优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                             //禁止M2M模式
DMA_Init(USART1_RX_DMA_CH, &DMA_InitStructure);                          //初始化DMA通道
//DMA_ITConfig(USART1_RX_DMA_CH,DMA_IT_TC,ENABLE);                       //使能传输完成中断
DMA_Cmd(USART1_RX_DMA_CH, ENABLE);                                       //开启DMA通道



//======USART1 TX的DMA通道配置========
DMA_DeInit(USART1_TX_DMA_CH);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART1->DR));    //外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_DMA_txbuf;       //存储器基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                       //传输方向:外设是数据目的地,存储器->外设
DMA_InitStructure.DMA_BufferSize = USART1_DMA_TXBUF_SIZE;                //传输长度:DMA_CNDTRx寄存器的值
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;         //外设地址生成算法,外设地址不增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                  //存储器地址生成算法,存储器地址增加
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //外设寄存器单个数据宽度:Byte代表8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;          //存储器单个数据宽度:Byte代表8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                            //单次传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;                      //通道的软件优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                             //禁止M2M模式
DMA_Init(USART1_TX_DMA_CH, &DMA_InitStructure);                          //初始化DMA通道
//DMA_ITConfig(USART1_TX_DMA_CH,DMA_IT_TC,ENABLE);                       //使能传输完成中断
//DMA_Cmd(USART1_TX_DMA_CH, ENABLE);                                     //开启DMA通道

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

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

相关文章

北京大学数学课程相关视频

今天无意中发现了一个网站:北京大学教学媒体资源服务平台媒体资源服务平台-北京大学,发现里面有一些精品的课程,于是将里面的数学相关可成整理出来。 北大精品课 离散数学(2013) 屈婉玲 课程链接:媒体资源…

Bugku-web-Simple_SSTl_1

开启环境 翻译一下 他说 您需要输入一个名为flag的参数。 SSTL 是一个模板注入, SECRET KEY:是flask 一个重要得配置值 需要用以下代码来加密 /?flag{{config.SECRET KEY}}(注意大小写),或直接 /?flag{{config}} 关于SSTL注入 SSTL注入,通常指的是‌服务器端…

Vxe UI vue vxe-table 实现表格数据分组功能,根据字段数据分组

Vxe UI vue vxe-table 实现表格数据分组功能&#xff0c;根据字段数据分组 实现数据分组功能 基于树结构功能就可以直接实现数据分组功能&#xff0c;代码如下&#xff1a; <template><div><vxe-button status"primary" click"listToGroup()&…

Self-Attention自注意力机制解读(2):图解版!

文章目录 一、前言二、流程解读1.它整体做了一件什么事2.多层Self-attention3.self-attention做了一件什么事4.具体流程 三、流程的矩阵表示三、Softmax层的解释 一、前言 上一篇文章 Self-Attention自注意力机制&#xff1a;深度学习中的动态焦点 | 手把手实例解析 看不懂你打…

Keepalived实验

keepalived在架构中的作用 LVS和HAProxy组成负载均衡 调度器LVS&#xff08;四层&#xff09;后端服务器&#xff08;多&#xff09; LVS&#xff1a;优点是速度快&#xff0c;性能要求不高&#xff0c;但是没有对后端服务器的健康检测&#xff1b; HAProxy&#xff1a;有后…

有名管道 | 信号

匿名管道由于没有名字&#xff0c;只能用于具有亲缘关系的进程间通信。 为了克服这个缺点&#xff0c;就提出了有名管道&#xff08;FIFO&#xff09;&#xff0c;也称为命名管道、FIFO文件。 有名管道 FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见&#…

8个最佳iMacros替代方案(2024)

iMacros作为一款经典的网页自动化工具&#xff0c;多年来帮助无数用户自动化了繁琐的网页任务。然而&#xff0c;随着技术的进步&#xff0c;越来越多功能更强大、界面更友好的替代方案涌现&#xff0c;为用户提供了更多选择。如果你正在寻找2024年最好的iMacros替代工具&#…

中国智能物流系统集成玩家图谱:很全

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 上海欣巴自动化科技股份有限公司 总部&#xff1a;上海服务行业&#xff1a;快递、电商、服装、零售、医药、食品饮料等解决方案&#xff1a;自动化分拣输送系统&#xff1b;控制软件…

linux下tomcat nio 底层源码调用系统函数处理流程

linux tomcat使用nio处理请求 调用jvm native C 底层调用系统函数源码原理 ##Acceptor接受socket socket endpoint.serverSocketAccept(); ##NioEndpoint serverSock.accept() ##ServerSocketChannelImpl n accept(this.fd, newfd, isaa); ##ServerSocketChannelImpl a…

Linux的安装和使用

Linux 第一节 Linux 优势 1. 开源 为什么这么多的的设备都选择使用 Linux&#xff1f;因为它是开源软件&#xff08;open source software&#xff09;&#xff0c;具有不同的含义。使用一个安全的操作系统工作变得必不可少的事&#xff0c;而 Linux 恰好满足了这个需求。因…

Golang | Leetcode Golang题解之第344题反转字符串

题目&#xff1a; 题解&#xff1a; func reverseString(s []byte) {for left, right : 0, len(s)-1; left < right; left {s[left], s[right] s[right], s[left]right--} }

vue3ts+element-plus实现点击el-select下拉选择内容填充和编辑内容

需求在填写报表时&#xff0c;既可以选择下拉选项&#xff0c;还可以编辑选的内容&#xff0c; 找了elementUi没有现成的就自己组装一个 效果&#xff1a; 贴代码&#xff1a; 在components下新建文件夹TextareaSelect&#xff0c;再新建index.vue和interface.ts &#xff08;…

AIGC创新应用技术实践:成都技术生态沙龙全回顾

AIGC创新应用技术实践&#xff1a;成都技术生态沙龙全回顾 2024年8月17日下午&#xff0c;我有幸作为CSDN校园主理人参加了在成都举办的AIGC创新应用技术实践沙龙活动。 此行也见到了许许多多的行业大佬&#xff0c;得到的收获非常之多&#xff0c;赶了1300公里的路&#xff0…

getActivePinia was called with no active Pinia

如果你是从vuex转向使用pinia&#xff0c;那么你可能遇到这个问题getActivePinia was called with no active Pinia. Did you forget to install pinia?。明明已经安装了pinia&#xff0c;为什么会有这个提示呢&#xff1f; 原因是你可能在setup的组件之外使用了useStore 根本…

JavaScript初级——运算符

一、算数运算符 1、运算符也叫操作符。通过运算符可以对一个或多个值进行运算&#xff0c;并获取运算结果。 比如&#xff1a;typeof 就是运算符&#xff0c;可以获得一个值的类型&#xff0c;他会将该值的类型以字符串的形式返回 &#xff08;number、string、boolean、undefi…

三防平板:现代生产效率与安全的革新者

三防加固工业平板电脑以其独特的防水、防尘、防摔性能&#xff0c;成为复杂生产环境下可靠的工作伙伴。无论是高温、高湿的恶劣环境&#xff0c;还是充满粉尘与振动的车间&#xff0c;三防平板电脑均能保持稳定运行&#xff0c;显著降低硬件故障率&#xff0c;确保生产线的平稳…

AI周报(8.11-8.17)

AI应用-小白&#xff0c;你在说什么呢&#xff1f;AI宠物翻译Traini Traini是由中国连续创业者孙邻家&#xff08;Arvin Sun&#xff09;和刘嘉懿&#xff08;Jack Liu&#xff09;于2022年在美国硅谷联合创立的全球宠物在线培训平台和宠物共情AI应用。Traini也在今年5月完成了…

MyBatis框架连接数据库-初级

MyBatis 的主要功能是帮助持久层管理数据库&#xff0c;接下来我们利用 MyBatis 连接一下我们的数据库&#xff0c;实现入门程序 前期准备 使用环境&#xff1a; jdk 17 maven 3.6.1 mybatis 3.5.10 mysql 8.0.31 idea 2021.1 需要准备&#xff1a; 一个 Maven 工程模块…

uniapp-部分文件中文乱码

一、问题 在开发时遇到&#xff0c;部分页面的中文显示乱码&#xff0c;如图 搜索了一下解决方法&#xff0c;这里记录一下 二、问题原因&#xff1a; 页面的编码格式不是 utf-8 造成的 三、解决方法 打开出现乱码页面选择编译器左上角的文件 > 以指定编码重新打开 选择U…

【存储学习笔记】4:快照(Snapshot)技术的实现方式

1 快照 1.1 动机 在上一篇《备份》里提到&#xff0c;热备份就是在执行操作时&#xff0c;服务器需要正常处理来自用户或应用对数据的更新&#xff0c;这样能够保证数据7*24小时可用&#xff08;在很多服务里这是必要的&#xff09;。 而热备份的困难就是如何保证数据的一致…