首页 > 其他分享 >STM32江科大————DMA数据转运(实现AD多通道)

STM32江科大————DMA数据转运(实现AD多通道)

时间:2025-01-19 23:32:31浏览次数:3  
标签:DMA 存储器 多通道 STM32 地址 InitStructure ADC 外设

声明:本人跟随b站江科大学习,本文章是观看完视频后的一些个人总结和经验分享,也同时为了方便日后的复习,如果有错误请各位大佬指出,如果对你有帮助可以点个赞小小鼓励一下,本文章建议配合原视频使用❤️

如果你也正在学习STM32可以订阅本专栏,后续将不定期更新( ˘ ³˘)❤️
在这里插入图片描述


文章目录

前言

还是在观看文章之前给大家留下几个问题,你可以在读完文章后问问自己有没有弄清楚这几个问题,如若清楚了,那么恭喜你已经基本了解了DMA的使用

  • 外设和存储器概念
  • DMA的作用
  • DMA的转运实际上是地址的转运,为什么
  • 软件触发与外部中断的软件触发的区别
  • 什么是特定的硬件触发
  • 站点的三个参数如何选择
  • 如何根据实际情况配置DMA

理论部分

1.DMA简介

在这里插入图片描述

  • 可以直接访问运行内存SRAM,程序存储器Flash,寄存器等存储器
  • 外设:一般是数据寄存器DR(Digital Register)
  • 存储器:内存SRAM,程序存储器Flash
  • 外设到存储器的转运:由于外设的数据是有时机的,所以这里不能够随便转运,我们要采取硬件触发,例如要等ADC转换完成,串口收到数据,定时时间到之后硬件触发一次DMA才能使用DMA转运,触发一次转运一次。(类似中断的更新事件)
  • 存储器到存储器的转运:一般使用软件触发
  • 特定的硬件触发:对应的硬件触发有固定的通道,等下具体介绍
  • 这里的软件触发和外部中断以及ADC的软件触发不一样,等下解释
  • 该芯片只有DMA1

2.存储器映像

在这里插入图片描述

  • 计算机的组成:运算器,控制器,存储器,输入和输出设备五个部分,其中运算器和控制器合称CPU,其中核心部分是CPU和存储器。存储器中的核心部分是存储器的内容和存储器的地址,上述表就是STM32中所具有的所有类型的存储器和其地址
  • 起始地址:就是给存储器分配的固定开头地址,起始地址后面的地址都是存储器内存的扩展
  • STM32的ROM存储器的存储介质其实都是FLASH,但是我们一般说的Flash指的是主闪存,也就是程序储存器Flash
  • 选项字节:主要存储的是Flash的读保护,写保护和看门狗等等配置
  • 临时变量:定义的变量,数组,结构体等等统称为临时变量
  • 内核外设:NVIC,SysTick等等

在这里插入图片描述
在这里插入图片描述

  • STM32F103的CPU是32位的,其存储器有4G的寻址空间,但是存储器都是KB级别的,所以就有很多空间没有使用到,图中灰色部分[Reserved]都是没有使用到的地址
  • 中间一列是ROM,左边一列是RAM,右边一列是Peripherals外设
  • 可以发现外设的地址都是0x4000开头,然后后面的地址再分是哪一个寄存器,这样每一个外设寄存器包括其存储内容都有对应地址了,其他的存储器也都是一样的
  • BOOT引脚的选择控制启动区域
    在这里插入图片描述

3.DMA框图

在这里插入图片描述

  • 核心:包括CPU和内核外设
  • 红色:主动单元,拥有访问存储器的权限,其中Dcode是专门用来访问Flash的,系统总线是用来访问APB1,APB2,AHBx等等总线上的存储器的,DMA总线什么都可以访问,有3个DMA分支通向DMA总线,用绿色标记
  • 褐色:被动单元,只能被主动单元读写
  • 紫色:仲裁器,由于DMA总线只有一条,所以所有的分支都只能分时复用这一条总线,如果产生冲突就由仲裁器决定先后使用顺序,红色的总线矩阵里面也有仲裁器,如果DMA总线和系统总线都要访问同一个目标,那么DMA就会让CPU停止访问,仲裁其会将总线一半的带宽给CPU,使CPU正常工作
  • 黄色:AHB从设备也就是DMA的寄存器,DMA作为一个外设也有自己的配置寄存器,这个寄存器连接在了AHB总线上,用粉色标记
  • DAM既是总线矩阵的主动单元,读写其他寄存器,也是AHB总线的被动单元,被读写
  • Flash特殊情况下可以写,一般只能读,SRAM可以读写,外设寄存器是否能够读写要看手册,不过一般都是操作数据寄存器,可以读写

4.DMA基本结构

在这里插入图片描述

  • 图中红色框框就是我们的站点,一个是外设站点,一个是存储器站点,站点是存储要交换的数据的地方,究竟是外设到存储器还是存储器到外设,这个方向我们也有参数可以控制。我们也可以存储器到存储器,但是由于Flash是只读存储器,所以只能Flash到SRAM或者SRAM到SRAM。
  • 站点都有三个参数:起始地址,数据宽度,地址是否自增起始地址是前面讲的每个存储器特有的地址,用来确认数据从哪里来,到哪里去;数据宽度就是被交换数据的大小,这里宽度有三种,分别是字节Byte,半字Half Word,字Word,大小分别为8位,16位,32位,例如我们的AD值是12位的,这里就要选择半字的宽度;地址是否自增是指每次转运完成之后,起始地址是否自增,比如说我们的ADC_DR(ADC数据寄存器),这个转运后肯定不能自增吧,改了地址可能下次就去转运其他寄存器了,但是存储器肯定要自增吧,不然这次存在存储器这个地址,下次还是存在这个地址,上一次存进来的值就会被覆盖了,数据寄存器本来就不用担心被覆盖,它的值已经转运到存储器部分被记录下来了,直接读取存储器就能知道上一次存的值是什么。
  • 这个外设站点的起始地址也不一定非要是外设寄存器的,写存储器的也行,只要改变一下控制方向的参数就行了,而且进行存储器之间的转运的话,两个站点就都是存储器了
  • 传输寄存器是用来控制转运次数的,我们可以给该寄存器一个值,这个值会自减,减到0之后DMA就不会再转运了,并且减到0之后之前站点自增的地址又会回到增长之前
  • 自动重装器是用来使传输寄存器的起始值清零后复原的,如果不使用的话,就只能够进行一次转运,传输寄存器清零后就不能够再次转运了,也就是单次模式,如果使用,就能够一直转运,也就是循环模式
  • M2M(Memory to Memory):即存储器到存储器。当给M2M置1时就是软件触发,置0就是硬件触发,这个软件触发的逻辑是以最快的速度连续不断触发DMA使传输寄存器自减为0,完成这一轮的转换,所以这个模式不能和循环模式一起使用,不然DMA转运会停不下来。所以存储器到存储器的转运一般使用软件触发,因为存储器之间的转运不需要时机,而且越快越好,硬件触发则需要看时机,一般用于存储器和外设之间的转运
  • DMA转运的条件:使能DMA,传输寄存器不为0,有触发源(软件或者硬件)
  • 如果没有使用循环模式,那么传输寄存器自减清0之后就需要先失能DAM才能够再次设置初始值,再使能DMA使之启动转运

5.DMA请求

在这里插入图片描述

  • 这个图表示的是几个外设硬件触发产生的7个DMA请求
  • 红色部分是M2M和EN(cmd)使能,这里的排版比较奇怪,一般在数据选择器侧边的是控制通道的引脚,左边是通道,这里EN在侧边但是表示的是使能,M2M才是控制通道的引脚2
    在这里插入图片描述
  • 上图蓝色部分的也就是上面讲述过的特定的硬件触发,看图可知ADC1的DMA请求通道就是通道一,每一个外设硬件触发的请求通道都是固定对应的
  • 如果使用ADC请求通道,那么使能这个DMA通道就要用ADC_DMAcmd函数使能,使用定时器也有TIM_DMAcmd使能,使用什么外设就要用其对应的DMA使能函数使能DMA,如果三个通道都开启了,我们可以看到这个或门,理论上三个通道都是有效的,但是一般只开启一个
  • 最后七个DMA通道只有一个总线,这里也有一个仲裁器来控制优先级,默认的是通道号越小优先级越高,也可以在程序中配置优先级
  • DMA_cmd使能DMA转运,xxxDMA_cmd使能DMA请求通道

6.数据宽度与对齐

在这里插入图片描述

  • 源端宽度(转运起点),目标宽度(转运终点),传输数目(要转运几个数据)
  • B0[7:0]表示B0这个数据是8位的,B0[15:0]表示B0这个数据是16位的
  • 0x1,0x2是源端和目标的地址
  • 这个传输数目都为4,所以每一行都是,源端0x0处的B0数据传输到目标0x0处,源端0x1处的B1数据传输到目标0x1处,传输4个数据也就是一直到B4
  • 结论:
    当源端宽度=目标宽度时,数据高低位一一对应,eg.都是8位大小,0x0B0->0x0B0
    当源端宽度>目标宽度时,舍弃高位,低位对齐,eg.源端16位大小,目标8位大小,0x0B1B0->0x0B0
    当源端宽度<目标宽度时,高位补0,低位对齐,eg.源端8位大小,目标16位大小,0x0B0->0x000B0

7.DAM转运

软件触发

在这里插入图片描述
DMA的配置:定义两个SRAM数组,并把数组地址放进站点,两个站点地址都自增,转运7次,软件触发,单次模式(不用自动重装器),数据宽度示情况而定,优先级配置也示情况而定

硬件触发

在这里插入图片描述
DMA配置(以ADC为例):把ADC_DR的地址塞进外设站点,定义的数组塞进存储器站点,如果ADC是连续转换则设置为循环模式,非连续则设置为单次模式,通道有几个就转运几次,并且每一次转运完成都硬件触发一次DMA请求进行DMA转运(对应外设要对应器通道),ADC_DR地址不自增,存储器地址自增,数据宽度都为半字,优先级配置示情况而定

8.补充

  • 位段:两个位段区映射了外设寄存器和SRAM全部的位,也就是可以位寻址(外设寄存器和SRAM中每一位都分配了地址),操作位段区的地址就相当于单独操作外设或者SRAM的某一位,SRAM位段区的起始地址是0x2200,外设寄存器是0x4200
    在这里插入图片描述
  • 真实地址=基地址+偏移地址
    看下面代码可以发现固件库代码的条理非常清晰,除了第一个外设基地址是固定值,其他的基地址都是通过 上一级基地址+偏移 计算出来的,最后GPIOA是一个 指定地址强制转换结构,也就是把这个地址强制转换成一个结构体指针,这个地方实现偏移的方法非常巧妙,因为结构体里存放寄存器的顺序刚好对应GPIOA实际上寄存器顺序,也就是说只要结构体的地址刚好为GPIOA_BASE的地址,由于结构体内容的地址也是在结构体地址基础上的延续,那么结构体寄存器的顺序就刚好就实现了在GPIOA_BASE基地址上的偏移,从而得到每一个寄存器的真实地址
#define PERIPH_BASE           ((uint32_t)0x40000000)		//外设基地址
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)		//APB2总线基地址
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)	//GPIOA 基地址

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)	//GPIOA结构

代码部分

DMA数据转运

在这里插入图片描述

MyDMA.c

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;					//定义全局变量,用于记住Init函数的Size,供Transfer函数使用

/**
  * 函    数:DMA初始化
  * 参    数:AddrA 原数组的首地址
  * 参    数:AddrB 目的数组的首地址
  * 参    数:Size 转运的数据大小(转运次数)
  * 返 回 值:无
  */
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size
	
	/*开启时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参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;					//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA使能*/
	DMA_Cmd(DMA1_Channel1, DISABLE);	//这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}

/**
  * 函    数:启动DMA数据转运
  * 参    数:无
  * 返 回 值:无
  */
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数
	DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成
	DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}

MyDMA.h

#ifndef __MYDMA_H
#define __MYDMA_H

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);

#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"

uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};				//定义测试数组DataA,为数据源
uint8_t DataB[] = {0, 0, 0, 0};							//定义测试数组DataB,为数据目的地

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	
	MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);	//DMA初始化,把源数组和目的数组的地址传入
	
	/*显示静态字符串*/
	OLED_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);
		
	while (1)
	{
		DataA[0] ++;		//变换测试数据
		DataA[1] ++;
		DataA[2] ++;
		DataA[3] ++;
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataA
		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);		//显示数组DataB
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);
		
		Delay_ms(1000);		//延时1s,观察转运前的现象
		
		MyDMA_Transfer();	//使用DMA转运数组,从DataA转运到DataB
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataA
		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);		//显示数组DataB
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);

		Delay_ms(1000);		//延时1s,观察转运后的现象
	}
}

DMA实现AD多通道

AD.c

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];					//定义用于存放AD转换结果的全局数组

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		//开启DMA1的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_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);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);	//规则组序列1的位置,配置为通道0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;											//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							//模式,选择独立模式,即单独使用ADC1
	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_NbrOfChannel确定
	ADC_InitStructure.ADC_NbrOfChannel = 4;										//通道数,为4,扫描规则组的前4个通道
	ADC_Init(ADC1, &ADC_InitStructure);											//将结构体变量交给ADC_Init,配置ADC1
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;											//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;				//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//外设数据宽度,选择半字,对应16为的ADC数据寄存器
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			//外设地址自增,选择失能,始终以ADC数据寄存器为源
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;					//存储器基地址,给定存放AD转换结果的全局数组AD_Value
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			//存储器数据宽度,选择半字,与源数据宽度对应
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						//存储器地址自增,选择使能,每次转运后,数组移到下一个位置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							//数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
	DMA_InitStructure.DMA_BufferSize = 4;										//转运的数据大小(转运次数),与ADC通道数一致
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;								//模式,选择循环模式,与ADC的连续转换一致
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;								//存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;						//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);								//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA和ADC使能*/
	DMA_Cmd(DMA1_Channel1, ENABLE);							//DMA1的通道1使能
	ADC_DMACmd(ADC1, ENABLE);								//ADC1触发DMA1的信号使能
	ADC_Cmd(ADC1, ENABLE);									//ADC1使能
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
	
	/*ADC触发*/
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作
}

AD.h

#ifndef __AD_H
#define __AD_H

extern uint16_t AD_Value[4];

void AD_Init(void);

#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	AD_Init();					//AD初始化
	
	/*显示静态字符串*/
	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);		//显示转换结果第0个数据
		OLED_ShowNum(2, 5, AD_Value[1], 4);		//显示转换结果第1个数据
		OLED_ShowNum(3, 5, AD_Value[2], 4);		//显示转换结果第2个数据
		OLED_ShowNum(4, 5, AD_Value[3], 4);		//显示转换结果第3个数据
		
		Delay_ms(100);							//延时100ms,手动增加一些转换的间隔时间
	}
}

标签:DMA,存储器,多通道,STM32,地址,InitStructure,ADC,外设
From: https://blog.csdn.net/2401_87412882/article/details/145228636

相关文章

  • 多通道二维卷积手动版+pytorch版本
    文章目录1.百度链接手动版2.Pytorch版本1.百度链接手动版通过网盘分享的文件:conv2dtest.xlsx链接:https://pan.baidu.com/s/1q3McqwfcKO1iX-Ms0BfAGA?pwd=ttsu提取码:ttsu2.Pytorch版本pythonimporttorchimporttorch.nnasnnimporttorch.nn.funct......
  • STM32之CubeMX新建工程操作(十八)
    STM32F407系列文章- STM32CubeMX(十八)目录前言一、STM32CubeMX二、新建工程​编辑1.创建工程2.选择芯片型号3.Pinout引脚分配1.SYS配置2.RCC配置3.定时器配置4.GPIO引脚配置5.中断配置6.通讯接口配置7.插件Middleware配置4.Clock时钟树配置5.工程管理Proje......
  • STM32单片机学习记录(1.17)
    一、STM32        10.3- I2C通信外设        1. I2C外设简介        (1)STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担;        (2)支持......
  • STM32单片机的学习总结
    从计算机基础、寄存器知识、汇编指令、中断以及各外设驱动的开发,单片机底层经过这段时间的学习做一个总结。计算机组成计算机由输入设备、输出设备、控制器、运算器、存储器组成,存储器分为外部存储器、内部存储器、高速缓存、寄存器,在单片机底层开发中,主要使用寄存器对某一地......
  • 7 FDMA LOOP
    前言之前使用了XILINX提供的DMAIP做了一个小实验,完成了PL和PS端通过DDR的方式进行数据交互,但是只能在XILINX的器件上使用,如果在一些国产FPGA板卡使用的话还需要使用的自己的IP,幸运的是,MLK团队他们的FDMA造福无数人,代码质量比较优秀,于是在网上找到一个FDMA3.2版本的进......
  • 数据搬运工DMA原理与实验
    STM32数据搬运工-DMASTM32-DMA工作原理DMA的概念:DMA,全称为:DirectMemoryAccess,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。......
  • 【STM32-学习笔记-7-】USART串口通信
    文章目录USART串口通信Ⅰ、硬件电路Ⅱ、常见的电平标准Ⅲ、串口参数及时序Ⅳ、STM32的USART简介数据帧起始位侦测数据采样波特率发生器Ⅴ、USART函数介绍Ⅵ、USART_InitTypeDef结构体参数1、USART_BaudRate2、USART_WordLength3、USART_StopBits4、USART_Parity5、USART......
  • 【STM32-学习笔记-8-】I2C通信
    文章目录I2C通信Ⅰ、硬件电路Ⅱ、IIC时序基本单元①起始条件②终止条件③发送一个字节④接收一个字节⑤发送应答⑥接收应答Ⅲ、IIC时序①指定地址写②当前地址读③指定地址读Ⅳ、MPU6050---6轴姿态传感器(软件I2C)1、模块内部电路2、寄存器地址3、软件模拟IIC①......
  • 【STM32-学习笔记-9-】SPI通信
    文章目录SPI通信Ⅰ、SPI通信概述1、SPI技术规格2、SPI应用3、硬件电路移位示意图Ⅱ、SPI时序基本单元①、起始条件②、终止条件③、交换一个字节(模式0)④、交换一个字节(模式1)⑤、交换一个字节(模式2)⑥、交换一个字节(模式3)Ⅲ、SPI时序①、发送指令②、指定地址写③、指......
  • 股票API接口使用python、JAVA等多种语言实例代码演示免费获取实时数据、历史数据、CDM
    ​最新整理的股票API接口,下方所有接口链接均可直接点击验证,查看返回的数据。沪深两市股票列表股票API接口链接(可点击验证):https://api.mairui.club/hslt/list/LICENCE-66D8-9F96-0C7F0FBCD073【实时数据接口】沪深两市实时交易数据接口股票API接口链接(可点击验证):https:......