STM32-DMA(软件出发、硬件触发)

2024-04-19 21:52

本文主要是介绍STM32-DMA(软件出发、硬件触发),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DMA  --为cpu减负

DMA简介

直接存储器存取(DMA)用来提供在外设存储器之间或者存储器和存储器之间的高速数据传输无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调DMA请求的优先权。

存储器和存储器之间的数据转运用软件触发

外设到存储器的数据转运,一般使用硬件触发。

STM32103C8T6 DMA资源:只有DMA1(7个通道)

存储器映像

计算机系统的五大组成部分:运算器、控制器、存储器、输入设备、输出设备,

CPU:运算器和控制器

ROM就是只读存储器,是一种非易失性掉电不丢失的存储器

RAM就是随机存储器,是一种易失性、掉电丢失的存储器

程序存储器Flash,也就是主闪存

DMA主要特性

12个独立的可配置的通道(请求)DMA17个通道,DMA25个通道

● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。

● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推)

● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。

● 支持循环的缓冲器管理

● 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

● 存储器和存储器间的传输

● 外设和存储器、存储器和外设之间的传输

● 闪存、SRAM、外设的SRAMAPB1APB2AHB外设均可作为访问的源和目标。

● 可编程的数据传输数目:最大为65535

DMA框图

总线矩阵的左端,是主动单元,也就是拥有存储器的访问权。右边的这些,是被动单元,他们的存储器只能被左边的主动单元读写。主动单元 这里,内核有DCode和系统总线,可以访问右边的存储器。DCode总线是专门访问Flash的,系统总线是访问其他东西的。另外,由于DMA要转运数据,所以DMA也必须要有访问的主动权。那主动单元,除了内核CPU,剩下的就是DMA总线了。

DMA1有一条总线,DMA也有一条总线,下面还有一条DMA总线,这是以太网外设自己私有的DMA,这个可以不用管。

DMA1:有7个通道;DMA2:有5个通道。各个通道可以分别设置他们转运数据的源地址和目的地址,这样它们就可以独立的工作

DMA,即使总线矩阵的主动单元,可以读写各种寄存器,也是AHB总线上的被动单元。CPU通过这一条线路,就可以对DMA进行配置了。

DMA请求:请求就是触发的意思,这条线路右边的触发源,是各个外设,所以DMA请求就是DMA的硬件触发源。(比如ADC转换完成、串口接收到数据,需要触发DMA转运数据的时候,就会通过这条线路,向DMA发出硬件触发信号,之后DMA就可以执行数据转运的工作了)

DMA基本结构

数据转运的两大站点:

外设寄存器站点、存储器站点(Flash,SRAM)

在这里可以看出,DMA的数据转运,可以是从外设到存储器,也可以从存储器到外设。另外,还有一种转运方式,就是存储器到存储器。由于Flash是只读的,所以DMA不可以进行SRAM到Flash,或者Flash到Flash的转运操作

然要进行数据转运,那肯定就要指定从哪里转到哪里,所以外设和存储器两个站点,就都有3个参数第一个参数:外设起始地址、存储器起始地址,这两个参数决定了数据是从哪里来,到哪里去。第二个数据参数:数据宽度,这个参数的作用是,指定一次转运要按多大的数据宽度来进行转运。它可以选择字节、半字、字。字节是8位,也就是一次转运一个uint8_t,这么大的数据;半字是16位,就是一次转运一个uint16_t这么大的数据;字是32位,就是一次转运一个uint32_t这么大的数据。(比如转运ADC的数据,ADC的结果是uint6_t这么大的数据,所以这个参数就要选择半字)。第三个参数:地址是否自增,这个参数的作用就是,指定一次转运完成后,下一次转运,是不是要把地址移动到下一个位置。比如ADC扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器这边,显然地址是不用自增的,如果自增,那下一次转运就跑到别的寄存器哪里了。存储器,地址就需要自增,每转运一个数据后,就往后挪个坑,要不然下次在转就把上次的覆盖掉了。)

传输计数器

这个东西就是用来指定,我们总共需要转运多少次。他是一个自减计数器(比如你写一个5,那DMA就只能进行5次数据转运,转运过程中,每转运一次,计数器的数就会减1,当减到0之后,DMA就不会进行数据转运了。另外,它减到0之后,之前自增的地址,就会恢复到起始地址的位置,以方便之后DMA开始新一轮的转运

自动重装器

传输计数器减到0之后,是否要自动恢复到最初的值(比如最初传输计数器给5,如果不使用自动重装器,那转运5次后,DMA就结束了;如果使用,就恢复到初值5)。

触发源:

硬件触发、软件触发,具体选择那个,由M2M这个参数决定(M2M是存储器到存储器的意思),当给M2M位1时,DMA就会选择软件触发,这个软件触发并不是调用某一个函数一次,触发一次,他这个软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,争取早日把传输计数器清零,完成这一轮的转换(软件触发不能和自动重装器同时使用,如果同时使用,DMA就停不下来了)。软件触发一般适用于存储器到存储器的转运,因为存储器到存储器的转运,是软件启动、不需要时机,并且想尽快完成的任务,所以M2M位给1,就是软件触发。当M2M位给0,那就是使用硬件触发。硬件触发源可以选择ADC、串口、定时器等等。使用硬件触发的转运,一般都是与外设有关的转运,这些转运需要一定的时机(比如ADC转换完成、串口收到数据、定时时间到等),所以需要使用硬件触发,在硬件达到这些时机时,传个信号过来,来触发DMA进行转运。

开关控制(DMA_Cmd函数):

当给DMA使能后,DMA就准备就绪,可以进行转运了。DMA进行转运,有几个条件。第一:就是开关控制,DMA_Cmd必须使能;第二:传输计数器必须大于0;第三:必须有触发源信号,触发一次,转运一次,传输计数器自减一次,当传输计数器等于0,且没有自动重装时,这时无论是否触发,DMA都不会在进行转运了。此时就需要DMA_Cmd,给DISABLE,关闭DMA,在为传输计数器写入一个大于0的数,在DMA_Cmd,给ENABLE,开启DMA。(注意:写传输计数器时,必须要先关闭DMA,在进行,不能在DMA开启时,写传输计数器)。

DMA请求

上面做这张图这是DMA1的请求映像,有7个通道。每个通道都有一个数据选择器,可以选择硬件触发或软件触发。

EN控制位就相当于开关控制:EN=0时不工作,EN=1时工作。

当M2M位=1时,选择软件触发。

通道1的硬件触发时ADC1、定时器2的通道3和定时器4的通道1,触发源应该怎么选择是对应的外设是否开启了DMA输出来决定的(比如使用ADC1,哪会有个库函数叫ADC_DMACmd,必须使用这个库函数开启ADC1的这一路输出,它才有效;如果现在定时器2的通道3,那也会有TIM_DMACmd函数,用来进行DMA输出控制)。总之触发源,具体使用那个,就把那个外设的DMA输出开启。之后,这7个触发源(DMA1有7个通道),进入到仲裁器,进行优先级判断,最终产生内部的DMA1请求(默认优先级是通道号越小,优先级越高)。

数据转运+DMA(软件触发)

将SRAM里的数组DataA转运到另一个数组DataB中

地址自增:两个数组地址都要自增

方向:外设站点转运到寄存器站点

传输计数器:在这里,显然要转运7次,所以传输计数器给7,自动重装暂时不需要。

触发选择:软件触发,因为这是存储器到存储器的数据转运,不需要等待硬件时机的,尽快转运完成就行了。最后调用DMA_Cmd,给DMA使能。这里的转运是一种复制转运,转运完成后DataA的数据并不会消失。

代码例程

接线图

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.H"
#include  "dma.h"uint8_t data[] = {0x01,0x02,0x03,0x04};  //源地址数组
uint8_t buff[] = {0,0,0,0};  //作为目的地址使用,存放转运后的结果int main(void)
{MYDMA_Init((uint32_t)data,(uint32_t)buff,4);OLED_Init();OLED_ShowString(1,1,"data");OLED_ShowString(3,1,"buff");OLED_ShowHexNum(1,8,(uint32_t)data,8);OLED_ShowHexNum(3,8,(uint32_t)buff,8);OLED_ShowHexNum(2,1,data[0],2);OLED_ShowHexNum(2,4,data[1],2);OLED_ShowHexNum(2,7,data[2],2);OLED_ShowHexNum(2,10,data[3],2);OLED_ShowHexNum(4,1,buff[0],2);OLED_ShowHexNum(4,4,buff[1],2);OLED_ShowHexNum(4,7,buff[2],2);OLED_ShowHexNum(4,10,buff[3],2);while(1){data[0]++;data[1]++;data[2]++;data[3]++;OLED_ShowHexNum(2,1,data[0],2);OLED_ShowHexNum(2,4,data[1],2);OLED_ShowHexNum(2,7,data[2],2);OLED_ShowHexNum(2,10,data[3],2);OLED_ShowHexNum(4,1,buff[0],2);OLED_ShowHexNum(4,4,buff[1],2);OLED_ShowHexNum(4,7,buff[2],2);OLED_ShowHexNum(4,10,buff[3],2);Delay_ms(1000);MYDMA_Transfer();OLED_ShowHexNum(2,1,data[0],2);OLED_ShowHexNum(2,4,data[1],2);OLED_ShowHexNum(2,7,data[2],2);OLED_ShowHexNum(2,10,data[3],2);OLED_ShowHexNum(4,1,buff[0],2);OLED_ShowHexNum(4,4,buff[1],2);OLED_ShowHexNum(4,7,buff[2],2);OLED_ShowHexNum(4,10,buff[3],2);Delay_ms(1000);}
}
#include "stm32f10x.h"                  // Device header
#include "DMA.h"uint16_t MYDMA_size;void MYDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t size)
{MYDMA_size = size;//开启时钟  DMA是AHB总线的设备,所以要用AHB开启时钟的函数RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);//定义结构体DMA_InitTypeDef DMA_InitStruct = {0};//给结构体赋值DMA_InitStruct.DMA_BufferSize = size;DMA_InitStruct.DMA_DIR =  DMA_DIR_PeripheralSRC; //指定外设地址是源地址还是目的地址DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;DMA_InitStruct.DMA_MemoryBaseAddr = AddrB;DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;  //自动重装DMA_InitStruct.DMA_PeripheralBaseAddr = AddrA; //外设站点的基地址DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //传输字节DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;  //地址自增DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;//将参数写入寄存器//这个是存储器到存储器的转运,软件触发,通道可以任意选择DMA_Init(DMA1_Channel1,&DMA_InitStruct);   //使能DMADMA_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);
}
#ifndef _DMA_H_
#define _DMA_H_void MYDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t size);
void MYDMA_Transfer(void);#endif

ADC扫描模式+DMA(硬件触发)

在这里有7个通道,触发一次,7个通道一次进行AD转换,然后转换结果都放到ADC_DR数据寄存器里。在每个单独的通道转换完成后,进行一个DMA数据转运,并且目的地址进行自增,所以在这里DMA的配置就是,外设地址,写入ADC_DR这个寄存器的地址。存储器的地址,可以在SRAM中定义一个数组,然后把这个数组的地址当作存储器的地址。之后数据宽度,因为ADC_DR和SRAM数组,我们要的都是uint16_t的数据,所以数据宽度都是16位的半字传输。地址自增:外设地址不要自增,存储器地址自增。传输方向,是外设站点到存储器站点。传输计数器,这里通道有7个,所以需要计数7次。计数器是否自动重装,这里可以ADC的配置,ADC如果是单次扫描,那DMA的传输计数器就不要配置自动重装,如果ADC是连续扫描模式,那DMA的传输计数器就需要自动重装。

代码例程

接线图

#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,手动增加一些转换的间隔时间}
}
#include "stm32f10x.h"                  // Device headeruint16_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的位置,配置为通道0ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure;											//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							//模式,选择独立模式,即单独使用ADC1ADC_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;				//外设基地址,给定形参AddrADMA_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_ValueDMA_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就可以一直连续不断地工作
}
#ifndef __AD_H
#define __AD_Hextern uint16_t AD_Value[4];void AD_Init(void);#endif

这篇关于STM32-DMA(软件出发、硬件触发)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/918535

相关文章

Ubuntu 怎么启用 Universe 和 Multiverse 软件源?

《Ubuntu怎么启用Universe和Multiverse软件源?》在Ubuntu中,软件源是用于获取和安装软件的服务器,通过设置和管理软件源,您可以确保系统能够从可靠的来源获取最新的软件... Ubuntu 是一款广受认可且声誉良好的开源操作系统,允许用户通过其庞大的软件包来定制和增强计算体验。这些软件

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har

软件设计师备考——计算机系统

学习内容源自「软件设计师」 上午题 #1 计算机系统_哔哩哔哩_bilibili 目录 1.1.1 计算机系统硬件基本组成 1.1.2 中央处理单元 1.CPU 的功能 1)运算器 2)控制器 RISC && CISC 流水线控制 存储器  Cache 中断 输入输出IO控制方式 程序查询方式 中断驱动方式 直接存储器方式(DMA)  ​编辑 总线 ​编辑

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口

HomeBank:开源免费的个人财务管理软件

在个人财务管理领域,找到一个既免费又开源的解决方案并非易事。HomeBank 正是这样一个项目,它不仅提供了强大的功能,还拥有一个活跃的社区,不断推动其发展和完善。 开源免费:HomeBank 是一个完全开源的项目,用户可以自由地使用、修改和分发。用户友好的界面:提供直观的图形用户界面,使得非技术用户也能轻松上手。数据导入支持:支持从 Quicken、Microsoft Money

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类