【STM32】DMA直接存储器存取
1 DMA简介
DMA(Direct Memory Access)直接存储器存取
可以直接访问STM32的存储器的,包括运行SRAM、程序存储器Flash和寄存器等等
DMA可以提供外设寄存器和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
每个通道都支持软件触发和特定的硬件触发
存储器到存储器转运,需要软件触发,一股脑的转运
外设到存储器的转运,需要硬件触发,触发一次转运一次。
STM32F103C8T6 DMA资源:DMA1(7个通道)
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(Direct Memory Access)直接存储器存取 可以直接访问STM32的存储器的,包括运行SRAM、程序存储器Flash和寄存器等等 DMA可以提供外设寄存器和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节…...
Vue3-09-条件渲染-v-show 的基本使用
v-show 的作用 v-show 可以根据条件表达式的值【展示】或【隐藏】html 元素。v-show 的特点 v-show 的实现方式是 控制 dom 元素的 css的 display的属性, 因此,无论该元素是否展示,该元素都会正常渲染在页面上, 当v-show 的 条件…...
ArrayList与LinkLIst
ArrayList 在Java中,ArrayList是java.util包中的一个类,它实现了List接口,是一个动态数组,可以根据需要自动增长或缩小。下面是ArrayList的一些基本特性以及其底层原理的简要讲解: ArrayList基本特性: 动…...
位运算(、|、^、~、>>、<<)
分类 编程技术 1.位运算概述 从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(、-、*、/)都是叫位运算,即将符号位共同参与运算的运算。 口说无凭,举一个简单的例子来看下 CPU 是如何进…...
Centos7部署SVN
文章目录 (1)SVN概述(2)SVN与Samba共享(3)安装SVN(4)SVN搭建实例(5)pc连接svn服务器(6)svn图标所代表含义 (1)…...
Vue中this.$nextTick的执行时机
一、Vue中this.$nextTick的执行时机,整体可分为两种情况: 第一种:下一次 Dom 更新之后执行(即等待DOM更新结束之后,执行nextTick的延迟回调函数); 第二种:页面挂载后 (m…...
Unity中的ShaderToy
文章目录 前言一、ShaderToy网站二、ShaderToy基本框架1、我们可以在ShaderToy网站中,这样看用到的GLSL文档2、void mainImage 是我们的程序入口,类似于片断着色器3、fragColor作为输出变量,为屏幕每一像素的颜色,alpha一般赋值为…...
2 使用postman进行接口测试
上一篇:1 接口测试介绍-CSDN博客 拿到开发提供的接口文档后,结合需求文档开始做接口测试用例设计,下面用最常见也最简单的注册功能介绍整个流程。 说明:以演示接口测试流程为主,不对演示功能做详细的测试,…...
【数据库设计和SQL基础语法】--查询数据--聚合函数
一、聚合函数概述 1.1 定义 聚合函数是一类在数据库中用于对多个行进行计算并返回单个结果的函数。它们能够对数据进行汇总、统计和计算,常用于提取有关数据集的摘要信息。聚合函数在 SQL 查询中广泛应用,包括统计总数、平均值、最大值、最小值等。 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按序批量操作大量数据(Java、springboot、mybatisplus、ElasticSearch) 以同步全量MySQL数据到ElasticSearch为例。 核心代码 业务逻辑: public boolean syncToElasticsearch() {log.info("Starting data synchronization to El…...
strict-origin-when-cross-origin
严格限制同源策略 (1)允许服务器的同源IP地址访问 (2)允许Referer --- 后端服务器要配置...
【置顶】 本博博文汇总
文章目录 前言音视频ijkplayer源码分析FFmpeg、音视频协议Andriod系统音视频框架C、C Android&Java源码分析、绘制、渲染Dalvik、Art虚拟机Java并发 计算机基础操作系统计算机网络设计模式、数据结构、算法 前言 23年底了,想来也工作十年,也一直在c…...
react.js源码二
三、调度Scheduler scheduling(调度)是fiber reconciliation的一个过程,主要决定应该在何时做什么?在stack reconciler中,reconciliation是“一气呵成”,对于函数来说,这没什么问题,因为我们只想要函数的运行结果,但对于UI来说还需要考虑以下问题: 并不是所有的state更…...
如何学习英语
前言 首先写一些自己的感言吧,其实从大学的时候就在不断地听英语,学英语,但是到毕业十几年后,英语一直没起到什么作用,当然最有作用的时候就是几次英语面试吧。 工作之后有一段学习英语的经历,当时花费了…...
robot测试自动化
一. 安装 黑羽robot 首先确保你电脑上安装好了 Python 3.7 或者 3.8 版本的解释器 hyrobot 使用说明1 | 白月黑羽 安装RF 黑羽robot基于Robot Framework ,所以必须先安装RobotFramework 直接执行如下Pip命令即可: pip install robotframework...
Linux---重定向命令
1. 重定向命令的介绍 重定向也称为输出重定向,把在终端执行命令的结果保存到目标文件。 2. 重定向命令的使用 命令说明>如果文件存在会覆盖原有文件内容,相当于文件操作中的‘w’模式>>如果文件存在会追加写入文件末尾,相当于文件…...
小区生活污水处理需要哪些设备和工艺
在小区生活中,污水处理是一个非常重要的环节,它关乎到环境的保护和居民的生活质量。因此,了解小区生活污水处理所需要的设备和工艺是至关重要的。 首先,在小区生活污水处理中,需要用到的设备包括污水收集系统、初级沉淀…...
【高性能计算】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格子,有空格分割提示)
作品展示: 背景需求: 1、以前做过一份比大小的题目 【教学类-05-01】20211018 Python VSC 大班 数字比大小(> <)_vsc比较3位数大小-CSDN博客文章浏览阅读674次。【教学类-05-01】20211018 Python VSC 大班…...
【Spark精讲】Spark与MapReduce对比
目录 对比总结 MapReduce流程 编辑 MapTask流程 ReduceTask流程 MapReduce原理 阶段划分 Map shuffle Partition Collector Sort Spill Merge Reduce shuffle Copy Merge Sort 对比总结 Map端读取文件:都是需要通过split概念来进行逻辑切片&…...
SQL错题集3
1.薪水第二多的员工的emp_no以及其对应的薪水salary limit a,b 其中a表示查询数据的起始位置,b表示返回的数量。 (MySQL数据库中的记录是从0开始的) 注意从0开始 2.员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth 聚合函数不能…...
Elasticsearch:使用 OpenAI 生成嵌入并进行向量搜索 - nodejs
在我之前的文章: Elasticsearch:使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation (一)(二)(三)(四) 我详细地描述了如何使用…...
[python高级编程]:02-类
此系列主要用于记录Python学习过程中查阅的优秀文章,均为索引方式。其中内容只针对本作者一人,作者熟悉了解的内容不再重复记录。 目录 01-装饰器 overload -- 方法重载 02-多态 多态和鸭子类型 03-设计模式 抽象基类和接口 01-装饰器 overload -- 方…...
java.lang.UnsupportedOperationException异常解决
在执行如下代码时,发现当apps.add("...");代码执行时,会报java.lang.UnsupportedOperationException错误 List<String> apps Arrays.asList("...");apps.add("..."); 问题出现的原因如下: 1、ArrayLi…...
openmediavault debian linux安装配置企业私有网盘(三 )——raid5与btrfs文件系统无损原数据扩容
一、适用环境 1、企业自有物理专业服务器,一些敏感数据不外流时,使用openmediavault自建NAS系统; 2、在虚拟化环境中自建NAS系统,用于内网办公,或出差外网办公时,企业内的文件共享; 3、虚拟化环…...
Two Phase Termination(两阶段)设计模式
Two Phase Termination设计模式是针对任务由两个环节组成,第一个环节是处理业务相关的内容,第二个阶段是处理任务结束时的同步、释放资源等操作。在进行两阶段终结的时候,需要考虑: 第二阶段终止操作必须保证线程安全。 要百分百…...
闲人闲谈PS之四十九——PLM和SAP集成常见的问题
惯例闲话:天气突变,没想到珠三角也骤降了10几度,昨晚还吹风扇模式,早上起来一下子感觉丝丝凉意。闲人还是喜欢冬天,冷,能让人思维清晰,提高工作效率。趁着天气适宜,赶紧加班擦屁股去…...
帆软BI目录
数据导入ORACLE库 写法 SELECT * FROM (SELECT a.id ,a.expandType,a.parentId,a.displayName,a.sortIndex,LEVEL lv ,replace(sys_connect_by_path(displayName,//),//Dec-Entry_Management//,) AS 路径FROM FINE_AUTHORITY_OBJECT aSTART WITH a.id decision-directory-ro…...
(第8天)保姆级 PL/SQL Developer 安装与配置
PL/SQL Developer 安装与配置(第8天) 咱们前面分享了很多 Oracle 数据库的安装,但是还没有正式使用过 Oracle 数据库,怎么连接 Oracle 数据库?今天就来讲讲我学习中比较常用的 Oracle 数据库连接工具:PL/SQL DEVELOPER。 PL/SQL Developer 的安装和配置对于新手来说还是…...
网站建设制作 企业站开发哪家好/外贸业务推广
解决办法:这样配置之后就可以了...
新乡市网站建设电脑培训班/长沙专业做网站公司
一、Scrapy简介 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 其最初是为了 页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回…...
域名注册商网站/seo 优化 工具
1. 完成之前系列文章涉及内容后,继续在命名提示符下运行rendom /prepare,此步骤主要是校验DC是否全部准备完成,如下图所示;2. 如果上述步骤中出现失误,比如发现新域名书写错误等,可以运行rendom /end&#…...
厦门做企业网站/免费推广网站大全
最短路问题需要记录路径时,可以另外开一个数组记录从哪个点走过来的。如果需要找出多条长度一样的最短路径,可以类似邻接链表的形式记录被更新的点,最后用dfs搜索。a * b % mod a % mod * b % mod乘法运算时要注意是否超上限long long类型最…...
企业头像logo设计/网站自然排名优化
一、GPT定时器简介 以前的延时函数采用空指令来实现,延时不准确且浪费cpu性能。当修改了6ull的主频以后,采用空指令延时函数就不准了。 因此需要一个高精度的延时函数,并且不随着主频的变化而改变。 stm32 使用 SYSTICK 这个硬件定时器来实现…...
企业网站备案怎么做/百度入口网址
去除重复字母 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 注意:该题与 1081 https://leetcode-cn.com/problems/smalle…...