DMA简介:

注:如果DMA进行的是存储器到存储器的数据转运,比如我们想把Flash里的数据转运到SRAM里去,那就需要软件触发了,使用软件触发之后,DMA就会一股脑地把这批数据,以最快的速度全部转运完成。

如果DMA进行的是外设(比如ADC采集的数据)到SRAM存储器的数据转运,就不能一股脑的运转了,因为外设的数据是有一定的时机的,所以这时我们需要用硬件触发,比如转运ADC的数据,那就得ADC每个通道AD转换完成后,硬件触发一次DMA之后DMA再再ADC序列中进行转运。

所以存储器到存储器的数据转运,我们一般使用软件触发,外设到存储器的数据转运,我们一般使用对应外设硬件触发。

注:上面写的是特定的硬件触发,是因为每个DMA的通道,它的硬件触发源是不一样的,你要使用某个外设的硬件触发源,就得使用它连接的那个通道,而不能任意选择通道。

存储器映像

计算机的五大组成部分是:运算器、控制器、存储器、输入设备和输出设备。

其中运算器和控制器一般会合在一起,叫做CPU,所以计算机的核心关键部分是CPU和存储器
存储器又有两个重要的知识点,一个是存储器的内容,另一个就是存储器的地址,
在上面的表里存储器分为两大类,ROM和RAM,ROM是只读存储器,是一种非易失性、掉电不丢失的存储器,RAM就是随机存储器,是一种易失性、掉电丢失的存储器
其中ROM分为三块,第一块是程序存储器Flash,也就是主闪存,存储C语言编译后的程序代码,如果以后在软件里看到,某个数据的地址是0800开头的,那你就可以确定,它是属于主闪存的数据,系统存储器和选项字节在ROM的最后,具体用途见表的右侧
RAM也分为三块,首先是运行内存SRAM,用途是存储运行过程中的临时变量,也就是我们在程序中定义变量、数组、结构体的地方,然后RAM区剩下的还有外设寄存器,用途是存储各个外设的配置参数,也就是我们初始化各个外设,最终所读写的东西,其实,外设寄存器也是存储器的一种,他的存储介质其实也是SRAM,只不过习惯把运行内存叫SRAM,而外设寄存器一般叫寄存器,最后的是内核外设寄存器,,用途是存储内核各个外设的配置参数,内部参数就是NVIC和SysTick,因为内核外设和其他外设不是一个厂家设计的,所以地址就被分开了。

注:在STM32中,所有的存储器都被安排到了0X00000000到0XFFFFFFFF,所以寻址范围是32位,最大可以支持4GB容量的存储器,而我们STM32的存储器都是KB级别所以这个4GB的寻址空间,会有大量的地址都是空的,在上图中,灰色的是Reserved区域,也就是保留区,没有使用到

DMA详细介绍

DMA框图如下:

图解:

左上角这里是Cortex-M3内核,里面包含了CPU和内核外设NVIC和SysTick等等,剩下的所有东西都可以看做存储器,所以总共就是存储器和CPU两个东西,Flash是主闪存,SRAM是运行内存,各个外设都可以看成是寄存器,也是一种SRAM存储器。
寄存器是一种特殊的存储器,一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面, 寄存器每一位的背后都连接了一根线,这些线可以用于控制外设电路状态,比如置引脚的高低电平、导通和断开开关、切换数据选择器或者多位结合起来当做计数器、数据选择器等等
为了让CPU高效有条理地访问存储器,这里设计了一个总线矩阵,总线矩阵的左端是主动单元,也就是拥有存储器的访问权,右边这些是被动单元只能被左边的主动单元读写,主动单元这里,内核有DCode和系统总线,可以访问右边的存储器,其中DCode总线是专门访问Flash的,系统是访问其他东西的,另外,由于DMA要转运数据,所以DMA也必须要有访问的主动权
这里DMA1有一条总线,DMA2也有一条总线,DMA1有七个通道,DMA2有五个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,这样它们就可以开始工作了
DMA里有一个仲裁器,虽然多个通道可以独立转运数据,但是最终DMA总线只有一条,所以所有通道都只能分时复用这一条DAM总线,如果产生了冲突,那么将由仲裁器根据通道的优先级来决定谁先用,谁后用
另外在总线矩阵里也有一个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突,不过总线仲裁器,任然会保持CPU得到一半的总线带宽,使CPU也能正常工作,这就是仲裁器的作用
下面是AHB从设备,也就是DMA自身的寄存器,因为DMA作为一个外设,它自己也会有相应的配置寄存器,这里连接到了总线右边的AHB总线上,所以DMA既是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元,CPU通过下面这条线路即可对DMA进行配置。

DMA请求含义:DMA请求就是DMA的硬件触发源,比如ADC转换完成,串口接收到数据,需要触发DMA转运数据的时候,就会通过DMA请求这条线路,向DMA发出硬件触发信号,之后DMA就可以执行数据转运到其它地方的工作了。
 注:1、CPU或者DMA直接访问Flash的话,是只可以读不可以写的

2、SRAM是运行内存,可以任意读写没有问题

3、外设寄存器要看参考手册里的描述,有的寄存器是只读的,有的寄存器是只写的,不过我们主要用的是数据寄存器,数据寄存器都是可以正常读写的

DMA工作时的大致框图

图解:

DMA的数据转运可以是从外设到存储器,也可以从存储器到外设,具体是向左还是向右,有一个方向的参数,可以进行控制,另外还有一种转运方式,就是存储器到存储器,比如Flash到SRAM或者SRAM到SRAM,这两种方式,由于Flash是只读的,所以DMA不可以进行SRAM到Flash或者Flash到Flash的转运操作
外设和存储器两个站点都有三个参数,第一个是起始地址,有外设端的起始地址,和存储器端的起始地址,这两个参数决定了数据是从哪里来到哪里去,第二个参数是数据宽度,这个参数的作用是指定一次转运要按多大的数据宽度来进行,它可以选择字节Byte、半字HalfWorld和字World,字节就是8位(uint8_t),半字是16位(uint16_t),字是32位(uint32_t),第三个参数是地址是否自增,这个参数的作用是指定一次转运完成后,下一次转运,是不是要把地址移动到下一个位置去,这就相当于指针,p++,比如ADC扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器这边,显然地址是不用自增的,如果自增,那下一次转运就跑到别的寄存器那里去了,而存储器这边,地址就需要自增,每转运一个数据,就往后移一下,要不然就把上次的覆盖掉了
下面的传输计数器,是用来指定总共需要转运几次的,这个传输计数器是一个自减计数器,比如你给他写一个5,那DMA就只能进行5次数据转运,转运过程中,每转运一次,计数器的数都会减一,当计数器减到0之后,DMA就不会再进行数据转运了,另外在减到0之后之前自增的地址,也会恢复到起始地址的位置,以方便DMA开始新一轮的转运
在传输计数器的右边,有一个自动重装器,传输计数器减到0之后,是否要自动恢复到最初的值,比如传输计数器给5,如果不使用自动重装器,那转运5次之后,DMA结束转运,如果使用主动重装器,在传输计数器减到0之后,就会立即重装到初始值5,即不重装,就是单次模式,重装就是循环模式
触发,就是决定DMA需要在什么时候进行转运,触发源有软件触发和硬件触发,决定选择哪个是由M2M(Memory to Memory)决定,当我们给M2M位1时,DMA就会选择软件触发(需要注意的是,这里的软件触发并不是调用函数一次触发一次,而是不断的触发DMA争取早日把计数器清零,可以理解为连续触发,所以软件触发和循环模式不能同时使用)
存储器到存储器的数据转运,M2M置1,软件触发
外设到存储器的数据转运,M2M置0,硬件触发
DMA_Cmd,使能DMA,DMA开始转运

1.单次传输和循环传输:在DMA中源地址中的数据传输之后,如果要关闭DMA再打开才能继续传输。在如果传输玩后自动开始下一次传输,就是循环传输。
2.触发选择:软件触发(连续触发)是尽快完成数据转运,适用于存储器到存储器(此时不应使用循环传输)。硬件出发适用于外设。

  • DMA转运的条件:

  1. 开关控制,DMA_Cmd必须使能
  2. 传输计数器必须大于0
  3. 触发源,必须有触发信号,硬件触发或者软件触发

重点:触发一次,转运一次,传输计数器自减一次,当传输计数器等于0,且没有自动重装(也就是没开启循环模式)时,这是无论是否触发,DMA都不会再转运了,这时就需要DMA_Cmd给DISABE,关闭DMA,再为传输计数器写一个大于0的数,再DMA_Cmd给ENABLE,开启DMA(必须先关闭DMA,再写入传输计数器),相当于重新开启DMA运作。

DMA的外设请求:

图解:

上图中的数据选择器的EN位并不是选择哪一路数据的选择位,而是决定这个通道(数据选择器)要不要工作,EN=0,数据选择器不工作,EN=1,数据选择器工作
软件触发后面跟个M2M位的意思是M2M为1时,软件触发
每个通道的硬件触发源不同,要根据相对应的触发源来选择通道
软件触发都一样,选择哪个都行
DMA触发源的选择是由这个外设是否开启了DMA输出来决定的,比如你要使用ADC1,那会有个库函数叫ADC_DMACmd,必须使用这个库函数开启ADC1这一路的输出,才有效

DMA数据转运的数据宽度及对齐

图解:

上表的大概意思收发数据位数相同,则正常转运,如果把大的数据转到小的里去,高位就会舍弃掉,如果把小的数据转到大的里面去,高位就会补0。

 实例介绍

配置过程:
1.开启DMA的时钟
2.初始化DMA(大部分项都义如其名,需要理解的有以下三个:
① DMA_Mode:传输模式,一次传输或循环传输,决定了传输计数器是否自动重装,即是否传输完后手动写传输寄存器。
② DMA_BufferSize:缓冲区大小,即传输计数器的值,每次传输几个数据
③DMA_M2M:是不是存储器到存储器,即硬件触发还是软件触发。存储器到存储器传输不能使用循环模式。
3.开启外设的DMA(如ADC_DMACmd),开启触发信号的输出
4.如需要,开启DMA的中断(DMA_ITConfig). 之前要在NVIC里配置相应的通道,编写中断服务函数
5.DMA_Cmd(); 使能DMA开始转运
6.用DMA_GetFlagStatus判断是否传输完成,记得手动清除标志位。
6.当使用单次传输,如果传输结束后需要再次传输,需要先失能DMA后写传输计数器DMA_SetCurrDataCounter再使能DMA;如果使用循环传输则不需要这句话

1:数据从SRAM通过DMA转运到SRAM

任务:将SRAM里的数组DataA,转运到另一个数组DateB

地址如图,宽度一致,传输计数器填7,不需要自动重装(也就是不需要循环),触发选择选软件触发

注:这里是复制转运,相当于把DateA的数据复制给DateB的位置

一些库函数介绍:DMA库函数

//恢复缺省配置
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);//DMA初始化
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;void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size = Size;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA配置初始化DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;  // Peripheral:外设。 外设地址DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //原地址按照一个字节8位DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;  //外设地址是否递增DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;  //内存地址DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  目标地址的数据宽度按照一个字节8位DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  地址是否递增DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //指定外设是源还是目标。destination:目的地DMA_InitStructure.DMA_BufferSize = Size;  //传输计数器大小DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //标准模式和循环模式两种DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;  //M2M:内存到内存的意思DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  //指定软件触发的优先级DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, DISABLE);  //模块启动失能
}void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);  //模块启动失能DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);  //设置传输中的数据单元数:DMA_BufferSize = Size;DMA_Cmd(DMA1_Channel1, ENABLE);//DMA1通道1模块启动使能while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);  //DMA1_FLAG_TC1 : DMA1通道1传输完成标志。DMA_ClearFlag(DMA1_FLAG_TC1);  //手动清除标志位
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};
uint8_t DataB[] = {0, 0, 0, 0};int main(void)
{OLED_Init();MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);OLED_ShowString(1, 1, "DataA");OLED_ShowString(3, 1, "DataB");OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);  //DataA的地址OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);  //DataB的地址while (1){DataA[0] ++;DataA[1] ++;DataA[2] ++;DataA[3] ++;OLED_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);OLED_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();OLED_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);OLED_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);}
}

 注:如果是硬件触发,不要忘记在相对应的外设里调用XXX——DMA_Cmd,开启一下触发信号输出,如果你需要DMA的中断,那就调用DMA_ITConfig,再在NVIC里配置相应的中断通道,写中断函数即可

ADC扫描模式+DMA

了解概念:
1.规则序列,有16个位置的菜单;注入序列:有4个位置的菜单。只有在规则序列存在的情况下才有注入序列,在规则序列转换的时候,注入序列有打断的权力,只有当注入序列转换完后规则序列才能继续。
2.ADC模式有两种,一种是单ADC,另一种是同时使用两个ADC,两个ADC一起工作,对同一个引脚交叉采样。
3.单次转换和连续转换。对于规则菜单和注入菜单,单次转换后只出个EOC标志位,需要手动触发才能继续转换。对于连续转换,每次菜单转换完后都会自己触发下一轮转换,一般都用连续转换。
4.是否扫描。非扫描模式下规则序列和注入序列中都是只有1个位置安排上了通道,也就是说转换的时候没有用到菜单。扫描模式下菜单里会填上很多通道,每次转换开始后挨个转换。

配置过程:
1.打开GPIO和ADC时钟
2.初始化GPIO
3.初始化ADC,注意以下两个选项:
①ADC_ContinuousConvMode:单次还是连续转换
②ADC_ScanConvMode:单个通道还是扫描菜单里的多个通道
4.设置ADC菜单里各个位置上的通道号及采样时间
5.如有需要,ADC_ITConfig配置中断,配置NVIC,编写中断服务函数
6.ADC_Cmd开启ADC
7.初始化ADC校准寄存器,等待校准寄存器初始化完成,ADC开始校准,等待校准完成。
8.ADC_SoftwareStartConvCmd软件触发ADC(单次转换每次调用,连续转换调用一次)或者硬件触发ADC(一般是用TIM触发ADC,也可以用外部引脚)

外设地址填ADC_DR
存储器的地址可以在SRAM中定义一个数组ADValue,然后把ADValue(数组名字本身是数组的首地址)当做存储器的地址
数据宽度都是16位的半字传输
外设地址不自增,存储器地址自增
方向是外设站点到存储器站点
计数七次
ADC如果单次扫描,不自动重装,如果ADC是扫描模式,那ADC就可以使用自动重装
DMA的触发要选择ADC的硬件触发

AD.c 

#include "stm32f10x.h"                  // Device headeruint16_t AD_Value[4];void AD_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);  //12MHZGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &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);ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //连续采集模式ADC_InitStructure.ADC_ScanConvMode = ENABLE;  //扫描模式ADC_InitStructure.ADC_NbrOfChannel = 4;ADC_Init(ADC1, &ADC_InitStructure);//DMA模块配置DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 4;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //循环模式DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //硬件触发DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);  //打通ADC和DMA的连接  启用或禁用指定的ADC DMA请求。ADC_Cmd(ADC1, ENABLE);//ADC校准模块ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);//ADC软件触发ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"int main(void)
{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){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. 【论文解读】DCN-M:Google提出改进版DCN,用于大规模排序系统的特征交叉学习(附代码)...

    " 本文结合DeepCTR-Torch中的代码实现,介绍了DCN的改进版--DCN-M.该模型能更有效地学习特征交叉,并通过低秩矩阵分解对参数矩阵进行降维,降低计算成本.受MOE结构启发,作 ...

  2. 超详细解读:神经语义解析的结构化表示学习 | 附代码分析

    在碎片化阅读充斥眼球的时代,越来越少的人会去关注每篇论文背后的探索和思考. 在这个栏目里,你会快速 get 每篇精选论文的亮点和痛点,时刻紧跟 AI 前沿成果. 点击本文底部的「阅读原文」即刻加入社区 ...

  3. Lua基础学习--附代码,运行截图

    Lua基础学习 一.lua简介 Lua [1] 是一个小巧的脚本语言.它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里 ...

  4. slim php dd model,第二十四节,TensorFlow下slim库函数的使用以及使用VGG网络进行预训练、迁移学习(附代码)...

    在介绍这一节之前,需要你对slim模型库有一些基本了解,具体可以参考第二十二节,TensorFlow中的图片分类模型库slim的使用.数据集处理,这一节我们会详细介绍slim模型库下面的一些函数的使用 ...

  5. 【详解】Transfer learning迁移学习 附代码

    迁移学习的训练策略: 1.先冻结卷积层只训练全链接层,这一步需要把结果最好的那个模型保存起来. 2.加载上一步保存的那个最优模型,在这个模型的基础上,再以更小的学习率训练所有层,更新网络的所有权重参数 ...

  6. 【32单片机学习】(6)STM32串口+DMA收发不定长数据

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1.DMA介绍 2.串口接收数据 3.实验现象 1.实验电路图 2.串口收发不定长数据视频演示 3.OLED 显示接收数据 ...

  7. 一文弄懂元学习 (Meta Learing)(附代码实战)《繁凡的深度学习笔记》第 15 章 元学习详解 (上)万字中文综述

    <繁凡的深度学习笔记>第 15 章 元学习详解 (上)万字中文综述(DL笔记整理系列) 3043331995@qq.com https://fanfansann.blog.csdn.net ...

  8. 【AI超级美发师】深度学习算法打造染发特效(附代码)

    [新智元导读]如今,在类似天天P图.美图秀秀等手机APP中,给指定照片或视频中的人物更换头发颜色已经是再正常不过的事情了.那么本文便介绍了该功能背后如AI头发分割模块.头发换色.颜色增强与修正模块等技 ...

  9. 《算法图解》学习笔记(七):狄克斯特拉算法(附代码)

    欢迎关注WX公众号:[程序员管小亮] python学习之路 - 从入门到精通到大师 文章目录 欢迎关注WX公众号:[程序员管小亮] [python学习之路 - 从入门到精通到大师](https://b ...

最新文章

  1. 安装了ubuntu14.04+windows7双系统的笔记本启动后出现grub rescue提示符
  2. Windows Server 2008防火墙问题及Sql Server2005用户登录问题
  3. 详细介绍Java和C++区别
  4. jQuery.merge与concat的区别
  5. 【日常小记】linux中强大且常用命令:find、grep
  6. 【结论】Number(jzoj(gz) 1781)
  7. mysql改原始密码mac_MAC版修改MySQL初始密码的方法
  8. c语言数组左下角便*,数据结构 - 数组
  9. python函数round()取整保留小数问题
  10. 8.15 SNAIL:神经注意力元学习
  11. node.js 设置 淘宝 镜像
  12. 最近两个星期,机器经常卡死,难道是内存用光了?
  13. 动态磁盘与基本磁盘的相互转换
  14. 时域采样与频域采样实验【matlab】
  15. 使用Cloudflare API动态解析域名IP
  16. 2021-02-14马克思主义概论
  17. excel学习-批量填充单元格
  18. 笔记本计算机待机后显示器,Win10笔记本电脑休眠唤醒后屏幕还是黑屏怎么办?...
  19. Ubuntu下Qt软件打包流程
  20. js插件的经典写法与总结

热门文章

  1. Cairngorm2/3 框架 烟水晶 HelloWorld 源代码案例
  2. java csv导出用excel打开乱码_java导出csv文件使用Excel打开乱码问题
  3. linux使用中遇到的问题
  4. S7-PLCSIM Advanced分布式通讯
  5. tenth day for learning
  6. PHP获取ip所在城市
  7. 线代——基础解系 vs 特征向量
  8. python爬虫抓取网站技巧总结
  9. 人工智能智能决策支持系统:技术、特点和挑战
  10. 国内数据库技术大牛:牛新庄博士自传(附:项目经验)