当前位置: 首页 > news >正文

【STM32】DMA直接存储器存取

1 DMA简介

DMA(Direct Memory Access)直接存储器存取

可以直接访问STM32的存储器的,包括运行SRAM、程序存储器Flash和寄存器等等

DMA可以提供外设寄存器和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

12个独立可配置的通道: DMA17个通道), DMA25个通道)

每个通道都支持软件触发和特定的硬件触发

存储器到存储器转运,需要软件触发,一股脑的转运

外设到存储器的转运,需要硬件触发,触发一次转运一次。

STM32F103C8T6 DMA资源:DMA17个通道)

1.1 存储器映像

类型

起始地址

存储器

用途

ROM

0x0800 0000

程序存储器Flash(主闪存)

存储C语言编译后的程序代码

(下载程序的位置)

0x1FFF F000

系统存储器

存储BootLoader,用于串口下载

0x1FFF F800

选项字节

存储一些独立于程序代码的配置参数

RAM

0x2000 0000

运行内存SRAM

存储运行过程中的临时变量

0x4000 0000

外设寄存器

存储各个外设的配置参数

0xE000 0000

内核外设寄存器

存储内核各个外设的配置参数

ROM:只读存储器,非易失性、掉电不丢失的存储器;

RAM:随机存储器,易失性,掉电丢失的存储器。

1.2 DMA框图

可以看作是CPU+存储器两个东西

Flash是主闪存,SRAM是运行内存。各个外设也可以看出存储器。

寄存器是连接软件和硬件的桥梁,软件读写寄存器相当于控制硬件的执行。

使用DMA转运归类为一类问题:从某个地址取值,再放到另一个地址。

利用总线访问,分为主动和被动。

DMA要有访问的主动权。

产生冲突时,由仲裁器根据通道的优先级决定先后顺序。

DMA既是总线矩阵的主动单元,可以读写各种寄存器,也是AHB上的被动单元。

Flash一般不能写入,需要配置才可以写入。

1.3 DMA的基本结构

左边是外设寄存器站点,右边是存储器站点,包括Flash和SRAM(存储器一般特指Flash和SRAM)。DMA的转运可以是从外设到存储器,也可以是从存储器到外设,有一个方向的参数可以控制;还有一种转运方式存储器到存储器。Flash到SRAM/SRAM到SRAM。

数据宽度的作用是指定一次转运要按多大的数据宽度来进行。

传输寄存器用来指定总共转运几次的,自减计数器。

重装寄存器:当传输寄存器减到0后,是否要自动恢复到最初的值。(单次/循环)

触发源有软件触发和硬件触发,由M2M(Memory to Memory)决定,软件触发的逻辑是:以最快的速度,连续不断的触发DMA,争取早点把传输计数器清零,完成这一轮的转换。软件触发和循环模式不能同时使用。(存储器到存储器的转运)

硬件触发源可以选择ADC、串口、定时器等(一般与外设转运有关)

DMA转运的条件:

(1)开关控制,DMA_Cmd必须使能;

(2)传输计数器必须大于0;

(3)必须要有触发源。

(4)以此循环,开启下一次,但是必须先关闭DMA,再给传输计数器传值。

1.4 DMA请求

DMA触发部分

每个通道都有一个数据选择器,选择硬件触发或者软件触发,EN=0数据选择器不工作;EN=1数据选择器工作。当M2M位=1时选择软件触发,M2M=0时选择硬件触发。

每个通道的触发源都是不一样的,即硬件触发要看通道,软件触发则是随意的。

通道1的硬件触发是ADC1、TIM2的通道3和TIM4的通道1,选择哪个触发源呢?根据外设是否开启的DMA输出来决定的,比如ADC1有个库函数,ADC_DMACmd。三个都开始是或门,一般开启一个。

7个触发源进入到仲裁器,进行优先级判断,最终产生内部的DMA1请求。(默认通道号越小,通道优先级越高,也可以配置)

1.5 数据宽度与对齐

如果转运的时候,数据宽度都一样,那就是正常的一个个转运;如果数据宽度不一样,则按这个表处理。

目标的数据宽度>源端数据宽度,在目标数据前面多出的地方补0;

目标的数据宽度<源端数据宽度,读B1B0,只写入B0;读B3B2,只写入B2,把多出的高位舍弃

1.6 数据转运+DMA

任务是:将SRAM里面的数组DataA搬运到另一个数组DataB中。

外设地址是DataA数组的首地址;存储器地址是DataB数组的首地址;

数据宽度,两个都是uint8_t;

两个站点的地址都自增;

方向参数:外设站点转运到存储器站点了。

传输计数器:7

自动重装暂时不需要;

使用软件触发(存储器到存储器)

复制转运DataA的数据不会消失。

1.7 ADC扫描模式+DMA

左边是ADC扫描模式的执行流程,触发一次7个通道依次进行AD转换,转换结果都放在ADC_DR数据寄存器里,需要做的是在每个单独的通道转换完成后,进行一次DMA数据转运,并且目的地址自增,所以DMA的配置是:外设地址写入ADC_DR这个寄存器的地址,存储器的地址可以在SRAM中定义一个数组ADValue,然后把ADValue的地址当作寄存器的地址;之后数据宽度是uint16_t。外设地址不自增,存储器地址自增;传输方向是外设站点到存储器站点;传输计数器是7;计数器是否重装根据ADC的配置,ADC单次扫描则传输计数器不重装;ADC循环扫描,则传输计数器自动重装;最后是触发选择,这里ADC_DR的值是在ADC单个通道转换完成之后才有效的,所以DMA转运的时机需要和ADC单个通道转换完成同步,所以DMA的触发选择ADC硬件触发。

ADC单个通道完成过不产生中断,但是会产生DMA请求,去触发DMA转运。

手册

测试

类型

起始地址

存储器

用途

ROM

0x0800 0000

程序存储器Flash(主闪存)

存储C语言编译后的程序代码

(下载程序的位置)

0x1FFF F000

系统存储器

存储BootLoader,用于串口下载

0x1FFF F800

选项字节

存储一些独立于程序代码的配置参数

RAM

0x2000 0000

运行内存SRAM

存储运行过程中的临时变量

0x4000 0000

外设寄存器

存储各个外设的配置参数

0xE000 0000

内核外设寄存器

存储内核各个外设的配置参数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"// 验证数据存储位置
uint8_t a = 0x66;				// 变量
const uint8_t b = 0x66;			// 只读不写,存在flash中int main()
{OLED_Init();								// 初始化OLEDOLED_ShowHexNum(1, 1, (uint32_t)&a, 8);		// 20000000,20开头OLED_ShowHexNum(2, 1, (uint32_t)&b, 8);		// 08000E28,08开头while (1){}
}

外设寄存器

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"int main()
{OLED_Init();										// 初始化OLEDOLED_ShowHexNum(1, 1, (uint32_t)&ADC1->DR, 8);		// 固定的,40001244C,40开头while (1){}
}

手册

加起来就是40001244C

函数一直跳转

2 DMA数据转运

2.1 接线图

2.2 模块封装

按这个初始化

库函数

void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);        // 恢复缺省配置
// 初始化
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
// 结构体初始化
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
// 使能
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
// 中断输出使能
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);// DMA设置当前寄存器,给传输计数器写数据的
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); // DMA获取当前数据寄存器,返回传输计数器的值
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);// 获取标志位状态、清除标志位状态、获取中断状、清除中断挂起位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
void DMA_ClearFlag(uint32_t DMAy_FLAG);
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);

MYDMA.c

#include "stm32f10x.h"                  // Device headeruint16_t MYDMA_Size;// DMA初始化
void MyDMA_Init(uint32_t addrA, uint32_t addrB, uint32_t size)
{MYDMA_Size = size;// 1开启DMA时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 2调用DMA_InitDMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = addrA;							// 外设站点的起始地址DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;		// 外设站点的数据宽度DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;				// 外设站点是否自增DMA_InitStructure.DMA_MemoryBaseAddr = addrB;								// 存储器站点的起始地址DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;				// 存储器站点的数据宽度DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						// 存储器站点是否自增DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							// 传输方向,外设作为源到存储器DMA_InitStructure.DMA_BufferSize = size;									// 缓冲区大小,传输计数器DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;								// 传输模式,是否自动重装,不重装DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;									// 软件触发还是硬件触发,软件触发DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;						// 优先级// DM1的通道1DMA_Init(DMA1_Channel1 ,&DMA_InitStructure);// 3DMA_Cmd,先失能,在转运中使能DMA_Cmd(DMA1_Channel1, DISABLE);
}// 传输函数
void MYDMA_Transfer(void)
{// 先DMA失能,再给传输计数器赋值DMA_Cmd(DMA1_Channel1, DISABLE);					// 失能DMA_SetCurrDataCounter(DMA1_Channel1, MYDMA_Size);	// 赋值DMA_Cmd(DMA1_Channel1, ENABLE);						// 使能while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	// 等待转运完成,没有完成一直等待DMA_ClearFlag(DMA1_FLAG_TC1);						// 清除标志位
}

2.3 主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};	// 源数组
uint8_t DataB[] = {0x00, 0x00, 0x00, 0x00};	// 目的数组int main()
{OLED_Init();									    // 初始化OLEDMyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);	// 初始化DMAOLED_ShowString(1, 1, "DataA:");OLED_ShowString(3, 1, "DataB:");OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);          // 显示地址OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);			// 显示地址// 第二行显示dataAOLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);// 第四行显示dataBOLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);while (1){DataA[0]++;         // 数据自增DataA[1]++;DataA[2]++;DataA[3]++;// 第二行显示dataAOLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);// 第四行显示dataBOLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);MYDMA_Transfer();          // 转运// 第二行显示dataAOLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);// 第四行显示dataBOLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);}
}

现象:dataA的数据每秒自增1,DMA将dataA的数据转运到dataB中。

3 DMA+AD多通道

3.1 接线图

同AD多通道

3.2 模块封装

版本1:ADC单次扫描+DMA单次转运的模式

ADC.c

#include "stm32f10x.h"                  // Device headeruint16_t AD_Value[4];// 初始化模块
void AD_Init(void)
{// 1开启RCC时钟,ADC、GPIO// 2配置GPIO输入模式// 3配置多路开关// 4配置ADC转换器// 开关控制// 校准RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 1RCC开启DMA时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);      // 72M / 6 = 12MGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        // 模拟输入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_Init(GPIOB, &GPIO_InitStructure);ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);// 4配置ADC转换器,单词转换,非扫描ADC_InitTypeDef ADC_InitStruct;ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStruct.ADC_ScanConvMode = ENABLE;ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;     // DISABLE单次转换  ENABLE连续转换ADC_InitStruct.ADC_NbrOfChannel = 4;ADC_Init(ADC1, &ADC_InitStruct);// 2调用DMA_InitDMA_InitTypeDef DMA_InitStruct;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;                             // 外设站点的起始地址DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;					       // 数据宽度DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;               						 // 地址不自增DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)AD_Value;                                 // 存储器站点的起始地址DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;               			// 数据宽度DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                       							// 地址自增DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                             // 传输方向:外设站点作为源头DMA_InitStruct.DMA_BufferSize = 4;                                       		// 数据宽度,取决传输方向DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                                  // 传输计数器是否自动重装(不自动重装)DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                          // 优先级DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                                   // 存储器到存储器(软件触发)// 第一个DMA的通道1DMA_Init(DMA1_Channel1, &DMA_InitStruct);// 3DMA_Cmd,DMA使能DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET);
}void AD_GetValue(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);            		// DMA先失能DMA_SetCurrDataCounter(DMA1_Channel1, 4);    		// 赋值DMA_Cmd(DMA1_Channel1, ENABLE);						// 使能ADC_SoftwareStartConvCmd(ADC1, ENABLE);             // 触发一次while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	// 等待转运完成,没有完成一直等待DMA_ClearFlag(DMA1_FLAG_TC1);           			// 清除标志位
}

版本2:ADC连续扫描+DMA循环转运的模式

ADC.c

#include "stm32f10x.h"                  // Device headeruint16_t AD_Value[4];// 初始化模块
void AD_Init(void)
{// 1开启RCC时钟,ADC、GPIO// 2配置GPIO输入模式// 3配置多路开关// 4配置ADC转换器// 开关控制// 校准RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 1RCC开启DMA时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);      // 72M / 6 = 12MGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        // 模拟输入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_Init(GPIOB, &GPIO_InitStructure);ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);// 4配置ADC转换器,单词转换,非扫描ADC_InitTypeDef ADC_InitStruct;ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStruct.ADC_ScanConvMode = ENABLE;ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;     // DISABLE单次转换  ENABLE连续转换ADC_InitStruct.ADC_NbrOfChannel = 4;ADC_Init(ADC1, &ADC_InitStruct);// 2调用DMA_InitDMA_InitTypeDef DMA_InitStruct;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;                             // 外设站点的起始地址DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;					       // 数据宽度DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;               						 // 地址不自增DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)AD_Value;                                 // 存储器站点的起始地址DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;               					// 数据宽度DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                       							// 地址自增DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                             // 传输方向:外设站点作为源头DMA_InitStruct.DMA_BufferSize = 4;                                       		// 数据宽度,取决传输方向// 循环模式DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                                  // 传输计数器是否自动重装(不自动重装)DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                          // 优先级DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                                   // 存储器到存储器(软件触发)// 第一个DMA的通道1DMA_Init(DMA1_Channel1, &DMA_InitStruct);// 3DMA_Cmd,DMA使能DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET);ADC_SoftwareStartConvCmd(ADC1, ENABLE);               // 触发一次
}//void AD_GetValue(void)
//{
//	DMA_Cmd(DMA1_Channel1, DISABLE);            					// DMA先失能
//	DMA_SetCurrDataCounter(DMA1_Channel1, 4);    					// 赋值
//	DMA_Cmd(DMA1_Channel1, ENABLE);												// 使能
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);               // 触发一次
//	
//	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);		// 等待转运完成,没有完成一直等待
//	DMA_ClearFlag(DMA1_FLAG_TC1);           							// 清除标志位
//}

3.3 主函数

版本1:ADC单次扫描+DMA单次转运的模式

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"int main()
{OLED_Init();       						// 初始化AD_Init();OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:");while (1){AD_GetValue();OLED_ShowNum(1, 5, AD_Value[0], 4);OLED_ShowNum(2, 5, AD_Value[1], 4);OLED_ShowNum(3, 5, AD_Value[2], 4);OLED_ShowNum(4, 5, AD_Value[3], 4);Delay_ms(100);}
}

版本2:ADC连续扫描+DMA循环转运的模式

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"int main()
{OLED_Init();       						// 初始化AD_Init();OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:");while (1){
//		AD_GetValue();OLED_ShowNum(1, 5, AD_Value[0], 4);OLED_ShowNum(2, 5, AD_Value[1], 4);OLED_ShowNum(3, 5, AD_Value[2], 4);OLED_ShowNum(4, 5, AD_Value[3], 4);Delay_ms(100);}
}

相关文章:

【STM32】DMA直接存储器存取

1 DMA简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 可以直接访问STM32的存储器的&#xff0c;包括运行SRAM、程序存储器Flash和寄存器等等 DMA可以提供外设寄存器和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&#xff0c;节…...

Vue3-09-条件渲染-v-show 的基本使用

v-show 的作用 v-show 可以根据条件表达式的值【展示】或【隐藏】html 元素。v-show 的特点 v-show 的实现方式是 控制 dom 元素的 css的 display的属性&#xff0c; 因此&#xff0c;无论该元素是否展示&#xff0c;该元素都会正常渲染在页面上&#xff0c; 当v-show 的 条件…...

ArrayList与LinkLIst

ArrayList 在Java中&#xff0c;ArrayList是java.util包中的一个类&#xff0c;它实现了List接口&#xff0c;是一个动态数组&#xff0c;可以根据需要自动增长或缩小。下面是ArrayList的一些基本特性以及其底层原理的简要讲解&#xff1a; ArrayList基本特性&#xff1a; 动…...

位运算(、|、^、~、>>、<<)

分类 编程技术 1.位运算概述 从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态&#xff0c;计算机对二进制数据进行的运算(、-、*、/)都是叫位运算&#xff0c;即将符号位共同参与运算的运算。 口说无凭&#xff0c;举一个简单的例子来看下 CPU 是如何进…...

Centos7部署SVN

文章目录 &#xff08;1&#xff09;SVN概述&#xff08;2&#xff09;SVN与Samba共享&#xff08;3&#xff09;安装SVN&#xff08;4&#xff09;SVN搭建实例&#xff08;5&#xff09;pc连接svn服务器&#xff08;6&#xff09;svn图标所代表含义 &#xff08;1&#xff09;…...

Vue中this.$nextTick的执行时机

一、Vue中this.$nextTick的执行时机&#xff0c;整体可分为两种情况&#xff1a; 第一种&#xff1a;下一次 Dom 更新之后执行&#xff08;即等待DOM更新结束之后&#xff0c;执行nextTick的延迟回调函数&#xff09;&#xff1b; 第二种&#xff1a;页面挂载后 &#xff08;m…...

Unity中的ShaderToy

文章目录 前言一、ShaderToy网站二、ShaderToy基本框架1、我们可以在ShaderToy网站中&#xff0c;这样看用到的GLSL文档2、void mainImage 是我们的程序入口&#xff0c;类似于片断着色器3、fragColor作为输出变量&#xff0c;为屏幕每一像素的颜色&#xff0c;alpha一般赋值为…...

2 使用postman进行接口测试

上一篇&#xff1a;1 接口测试介绍-CSDN博客 拿到开发提供的接口文档后&#xff0c;结合需求文档开始做接口测试用例设计&#xff0c;下面用最常见也最简单的注册功能介绍整个流程。 说明&#xff1a;以演示接口测试流程为主&#xff0c;不对演示功能做详细的测试&#xff0c;…...

【数据库设计和SQL基础语法】--查询数据--聚合函数

一、聚合函数概述 1.1 定义 聚合函数是一类在数据库中用于对多个行进行计算并返回单个结果的函数。它们能够对数据进行汇总、统计和计算&#xff0c;常用于提取有关数据集的摘要信息。聚合函数在 SQL 查询中广泛应用&#xff0c;包括统计总数、平均值、最大值、最小值等。 1…...

Module ‘app‘: platform ‘android-33‘ not found.

目录 一、报错信息 二、解决方法 一、报错信息 Module app: platform android-33 not found. 检查你的应用程序的build.gradle文件中的targetSdkVersion和compileSdkVersion是否正确设置为已安装的Android SDK版本。 确保你的Android Studio已正确安装并配置了所需的Android …...

MySQL按序批量操作大量数据

MySQL按序批量操作大量数据&#xff08;Java、springboot、mybatisplus、ElasticSearch&#xff09; 以同步全量MySQL数据到ElasticSearch为例。 核心代码 业务逻辑&#xff1a; public boolean syncToElasticsearch() {log.info("Starting data synchronization to El…...

strict-origin-when-cross-origin

严格限制同源策略 &#xff08;1&#xff09;允许服务器的同源IP地址访问 &#xff08;2&#xff09;允许Referer --- 后端服务器要配置...

【置顶】 本博博文汇总

文章目录 前言音视频ijkplayer源码分析FFmpeg、音视频协议Andriod系统音视频框架C、C Android&Java源码分析、绘制、渲染Dalvik、Art虚拟机Java并发 计算机基础操作系统计算机网络设计模式、数据结构、算法 前言 23年底了&#xff0c;想来也工作十年&#xff0c;也一直在c…...

react.js源码二

三、调度Scheduler scheduling(调度)是fiber reconciliation的一个过程,主要决定应该在何时做什么?在stack reconciler中,reconciliation是“一气呵成”,对于函数来说,这没什么问题,因为我们只想要函数的运行结果,但对于UI来说还需要考虑以下问题: 并不是所有的state更…...

如何学习英语

前言 首先写一些自己的感言吧&#xff0c;其实从大学的时候就在不断地听英语&#xff0c;学英语&#xff0c;但是到毕业十几年后&#xff0c;英语一直没起到什么作用&#xff0c;当然最有作用的时候就是几次英语面试吧。 工作之后有一段学习英语的经历&#xff0c;当时花费了…...

robot测试自动化

一. 安装 黑羽robot 首先确保你电脑上安装好了 Python 3.7 或者 3.8 版本的解释器 hyrobot 使用说明1 | 白月黑羽 安装RF 黑羽robot基于Robot Framework &#xff0c;所以必须先安装RobotFramework 直接执行如下Pip命令即可&#xff1a; pip install robotframework...

Linux---重定向命令

1. 重定向命令的介绍 重定向也称为输出重定向&#xff0c;把在终端执行命令的结果保存到目标文件。 2. 重定向命令的使用 命令说明>如果文件存在会覆盖原有文件内容&#xff0c;相当于文件操作中的‘w’模式>>如果文件存在会追加写入文件末尾&#xff0c;相当于文件…...

小区生活污水处理需要哪些设备和工艺

在小区生活中&#xff0c;污水处理是一个非常重要的环节&#xff0c;它关乎到环境的保护和居民的生活质量。因此&#xff0c;了解小区生活污水处理所需要的设备和工艺是至关重要的。 首先&#xff0c;在小区生活污水处理中&#xff0c;需要用到的设备包括污水收集系统、初级沉淀…...

【高性能计算】Cpp + Eigen + Intel MKL + 函数写成传引用

CUDA加速原理:CUDA编程学习:自定义Pytorch+cpp/cuda extension 高质量C++进阶[2]:如何让线性代数加速1000倍? 【gcc, cmake, eigen, opencv,ubuntu】三.eigen和mkl安装和使用 Linux下MKL库的安装部署与使用,并利用cmake编译器调用MKL库去提升eigen库的计算速度 Eigen库…...

【教学类-05-02】20231216 (比大小> <=)X-Y之间的比大小88题(补全88格子,有空格分割提示)

作品展示&#xff1a; 背景需求&#xff1a; 1、以前做过一份比大小的题目 【教学类-05-01】20211018 Python VSC 大班 数字比大小&#xff08;&#xff1e; &#xff1c;&#xff09;_vsc比较3位数大小-CSDN博客文章浏览阅读674次。【教学类-05-01】20211018 Python VSC 大班…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

Java多线程实现之Runnable接口深度解析

Java多线程实现之Runnable接口深度解析 一、Runnable接口概述1.1 接口定义1.2 与Thread类的关系1.3 使用Runnable接口的优势 二、Runnable接口的基本实现方式2.1 传统方式实现Runnable接口2.2 使用匿名内部类实现Runnable接口2.3 使用Lambda表达式实现Runnable接口 三、Runnabl…...