首页 > 其他分享 >STM32 DMA

STM32 DMA

时间:2024-07-20 20:01:25浏览次数:18  
标签:DMA ADC1 STM32 OLED ADC InitStruct GPIO

STM32暑假学习 DMA


文章目录


前言

DMA是ADC多通道输出的好帮手,类似于饭店里的服务员,能帮助外设和存储器直接搬运数据。


一、DMA是什么?

  • DMA(Direct Memory Access) 直接存储器存取
  • DMA可以提供外设和存储器或存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
  • 12个独立可配置的通道:DMA1(7个通道),DMA2(5个通道)
  • 每个通道都支持软件触发和特定的硬件触发
  • STM32F103C8T6 DMA资源:DMA1(7个通道)

存储器映像

在这里插入图片描述
STM32里的存储器和被安排的地址

二、DMA基本结构图

在这里插入图片描述
· 两大站点:外设寄存器站点和存储器站点(Flash和SRAM)
· 转运的方向有:外设到Flash和SRAM,Flash和SRAM到外设,Flash到SRAM
· 外设和存储器两个站点都有3个参数,起始地址、数据宽度、地址是否自增。
· 数据宽度:可以选择字节Byte(8位)、半字HalfWord(16位)和字Word(32位)
· 外设地址不用自增,存储器地址需要自增
· 如果外设起始地址写Flash地址,那他就会去Flash里去找数据。不用局限于一定要找外设寄存器。
· 传输计数器:记录需要转运的次数
· 自动重装器:例如传输计数器是5,需要执行5次转运,如果转运5次结束后,自动重装器开启了的话,那传输计数器自动恢复到5。类似于ADC中的连续模式
·M2M:Memory to Memory 当M2M等于1时,使用软件触发,软件触发一般适用于存储器到存储器的转运,是软件启动,不需要时机,并且向尽快完成任务。当M2M等于0时,使用硬件触发,硬件触发源可以选择ADC、串口、定时器等等,使用硬件触发的转运一般都是与外设有关的转运。这些转运需要一定的时机,比如ADC转换完成,串口收到数据,定时时间到等等,所以需要使用硬件触发,在硬件达到这些时机时,转一个信号过来触发控制

1.DMA进行转运,有以下条件:

1.开关控制,DMA_Cmd必须使能。
2.传输计数器必须大于0
3.触发源,必须有触发信号。触发一次,转运一次,传输计数器自减一次,当传输计数器等于0时,且没有自动重装时,这时无论是否触发,DMA都不会再进行转运了,此时需要DMA_Cmd给Disable,关闭DMA。再为传输计数器写入一个大于0的数,再开启DMA_Cmd,DMA才能继续工作。
·写传输计数器的时候,必须关闭DMA,再进行传输。不能在DMA 开启时写传输计数器。

2.数据宽度与对齐

如果目标的数据宽度比源端的数据宽度大,则高位补0。如果目标数据宽度比源端数据宽度小,则舍弃高位。

3.DMA是如何工作的

数据转运+DMA

在这里插入图片描述
· 将SRAM里的数组DataA,转运到另一个数组DataB中。
· 首先要填外设和存储器的起始地址,数据宽度,地址是否自增。
· 外设地址填DataA数组的首地址,存储器地址给DataB数组的首地址。
· 数据宽度,两组都是uint8_t
· 看需求,当DataA[0]转运到DataB[0]时,接下来是DataA[1]转到DataB[1],所以两个站点的地址都应该自增,都移动到下一个数据的位置
· 调用DMA_Cmd,给DMA使能
· 转运7次之后,传输计数器自减到0,DMA停止,转运完成

ADC扫描模式+DMA

在这里插入图片描述
左边ADC触发一次后,7个通道依次进行AD转换,然后转换结果都放到ADC_DR数据寄存器里面,在每个单独的通道转换完成后,进行一个DMA数据转运,并且目的地址进行自增,这样数据就不会被覆盖,外设地址,写入ADC_DR这个寄存器的地址,存储器的地址,可以在SRAM中定义一个数组ADValue,然后把ADValue的地址当做存储器的地址,之后数据宽度,因为ADC_DR和SRAM数组都是uint16_t,所以数据宽度都是16位的半字传输
· 从图可知,外设地址不自增,存储器地址自增,传输方向,是外设站点到存储器站点,传输计数器,这里通道有7个,所以计数7次
· 如果ADC是连续扫描,那DMA就可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮的转运,ADC和DMA同步工作
· 最后是触发选择,这里ADC_DR的值是在ADC单个通道转换完成后才会有效,所以DMA转运的时机,需要和ADC单个通道转换完成同步,所以DMA的触发要选择ADC的硬件触发
· 虽然单个通道转换完成后,不产生任何标志位和中断,但是它应该会产生DMA请求,去触发DMA转运
·一般来说,DMA最常见的用途就是配合ADC的扫描模式,因为ADC扫描模式有个数据覆盖的特征,或者可以说这个数据覆盖的问题是ADC固有的缺陷,ADC对DMA的需求是非常强烈

三、 数据转运+DMA示例代码及接线图

在这里插入图片描述

ADC1->DR
我们利用利用这种方式来访问到ADC的DR寄存器

DMA编程思路

· 第一步,RCC开启DMA的时钟
· 第二步,直接调用DMA_Init ,初始化各个参数,(外设和存储器的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否需要自动重装、选择触发源、通道优先级
· 第三步DMA_Cmd,进行开关控制
· 如果选择硬件触发,记得在对应外设调用一下XXX_DMACmd,开启一下触发信号的输出
· 如果需要DMA的中断,那就调用DMA_ITConfig,开启中断输出,再在NVIC里,配置相应的中断通道,写中断函数就行了
· 最后,在运行过程中,如果转运完成,传输计数器清0了,这时再想给传输计数器赋值的话,需要将DMA失能,写传输计数器、DMA使能。

DMA的库函数介绍

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);//中断输出使能 
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //设置当前数据寄存器
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);//清除中断挂起位

DMA初始化配置

#include "Device/Include/stm32f10x.h"   // Device header
uint16_t MyDMA_Size;

void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//开启RCC_AHB时钟
	
	DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = AddrA;//外设起始地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设地址是否自增
	DMA_InitStruct.DMA_MemoryBaseAddr = AddrB;//存储器起始地址
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据宽度
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器地址是否自增
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//DMA的传输方向,(外设作为源头,传给存储器)
	DMA_InitStruct.DMA_BufferSize = Size;//传输计数器的次数
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;//正常模式
	DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;//启用软件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;//DMA优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStruct);
	
	DMA_Cmd(DMA1_Channel1,DISABLE);//DMA失能
}

· 外设地址和存储器地址都设置为变量,根据调用函数的实际地址输入。
· DMA的传输方向可以选择外设作为目的地或者源头。
· 正常模式下不会开启自动重装,循环模式会开启自动重装
· M2M使能就是启用软件触发,失能就是启用硬件触发

DMA转运函数如下:

void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);//DMA失能
	DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);//设置数据寄存器
	DMA_Cmd(DMA1_Channel1,ENABLE);//DMA使能

	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//等待转运结束,标志位置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);
	OLED_ShowHexNum(3,8,(uint32_t)DataB,8);

	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);

	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);
	}
}

四、 ADC扫描模式+DMA示例代码及接线图

在这里插入图片描述

AD.c代码示例

#include "Device/Include/stm32f10x.h"   // Device header

uint16_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);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3;
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//点4个菜(选择4个通带)
	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_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode= DISABLE;//单次扫描
	ADC_InitStruct.ADC_DataAlign= ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel = 4;//通道数量为4个
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;//扫描模式开启
	ADC_Init(ADC1,&ADC_InitStruct);
	
	DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//端菜源头,在ADC->DR地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//想要低16位的数据,所以选半字
	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_M2M = DMA_M2M_Disable;//不使用软件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_InitStruct);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_DMACmd(ADC1,ENABLE);//开启ADC到DMA的触发信号
	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是单次模式所以每次转运都要查询写入一下传输计数器
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,4);
	DMA_Cmd(DMA1_Channel1,ENABLE);

	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//因为是ADC是单次模式所以还需要软件触发一下
	
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);//等待DMA转换完成

}

main.c示例代码

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

float Voltage;

int main(void)
{
	OLED_Init();
	AD_Init();
	OLED_ShowString(1,1,"AD0:");
	OLED_ShowString(2,1,"AD1:");
	OLED_ShowString(3,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[3],4);
		Delay_ms(100);
	}
}

ADC连续扫描+DMA循环转运模式

ad.c代码

#include "Device/Include/stm32f10x.h"   // Device header

uint16_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);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3;
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	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_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode= ENABLE;//****
	ADC_InitStruct.ADC_DataAlign= ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel = 4;
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;//****
	ADC_Init(ADC1,&ADC_InitStruct);
	
	DMA_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_M2M = DMA_M2M_Disable;
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_InitStruct);
	
	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);//******
}

ADC连续模式+DMA更加简洁,且可以直接查询到转换的数值

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,"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[3],4);
		Delay_ms(100);
	}
}

总结

DMA多用于跟ADC多通道转换,可以让定时器输出通向ADC,DAC或其他定时器,ADC的触发源可以来自定时器或外部中断,DMA的触发源可以来自ADC、定时器、串口等等

标签:DMA,ADC1,STM32,OLED,ADC,InitStruct,GPIO
From: https://blog.csdn.net/xiaohao777777/article/details/140516866

相关文章

  • 【记录】stm32f103c8t6+hc05+TB6612FNG实现蓝牙app控制直流电机
    前言这周刚好做了一个小项目,需要用到单片机控制一个小车移动,在实验室搜刮了一些材料,进行了一些调试工作,感觉也是蛮有意思的。小车的底盘用的是之前电赛剩下的,单片机用的是最小系统板,蓝牙模块是hc05,直流电机也是最普通的小马达。软硬件调试软件:keil5主控板:stm32f103c8t6蓝......
  • STM32学习(2)-GPIO输出
    GPIO输出2.1GPIO输出1.GPIO简介2.GPIO基本结构3.GPIO位结构4.GPIO模式5.硬件电路2.2LED闪烁&LED流水灯&蜂鸣器1.LED闪烁main函数代码2.LED流水灯3.蜂鸣器2.1GPIO输出1.GPIO简介GPIO(GeneralPurposeInputOutput)通用输入输出口可配置为8种输入输出模式引脚电......
  • STM32被拔网线 LWIP的TCP无法重连解决方案
    目录一、问题描述二、项目构成三、问题解决1.问题代码2.解决思路3.核心代码: 四、完整代码1.监测网口插入拔出任务2.TCP任务3.创建tcp任务4.删除tcp任务五、总结一、问题描述最近遇到一个问题,就是我的stm32设备作为tcp客户端和上位机交互,如果在连接过程中网线......
  • STM32+USART串口(1)
    GPIO口的复用功能是有对应的,作USART使用的话要选择对应的GPIO;可以参考引脚定义;(1)串口通信分为:串行通信和并行通信;(2)通信波特率:通常用波特率(BaudRate)来衡量数据通信的速度。波特率是指每秒钟传送数据的位数,单位为bps(BitPerSecond),用户可根据需要进行设定(3)异步通信:在异步通......
  • STM32的编码器接口如何实现倍频
    我们重点关注STM32的编码器接口是如何实现信号采集和倍频的。查STM32参考手册得到:接入编码器接口的是TI1FP1和TI2FP2。其中STM32的编码器接口在计数的时候,并不是单纯采集某一通道信号的上升沿或下降沿,而是需要综合另一个通道信号的电平。表中“相对信号的电平”指的就是在......
  • 【STM32芯片启动流程】——结合具体启动文件和hex文件分析
    一、前言最近想把MCU相关的知识梳理一遍,希望加深自己对相关知识的理解,同时也作为备忘录提醒自己。首先是STM32的启动过程,理解这个过程是学习IAP和OTA功能以及深入stm32内核的基础。二、总体流程介绍1.由boot引脚(boot0,boot1)选择启动模式;2.给SP、PC指针赋值;3.进入复位中......
  • STM32智能城市交通管理系统教程
    目录引言环境准备智能城市交通管理系统基础代码实现:实现智能城市交通管理系统4.1数据采集模块4.2数据处理与控制模块4.3通信与网络系统实现4.4用户界面与数据可视化应用场景:城市交通管理与优化问题解决方案与优化收尾与总结1.引言智能城市交通管理系统通过STM32嵌......
  • stm32 - IIC
    目录STM32-IIC1.基本概念2.引脚说明SDASCK/SCL3.传输方向4.通信过程1.空闲状态2.开始信号3.数据发送4.应答信号5.数据接收6.停止信号STM32-IIC1.基本概念半双工同步通信的串行通信接口2.引脚说明SDASCK/SCLIIC总线只需要两根引脚就可以实现通信,一根是数......
  • STM32除IO脚的其他引脚功能说明 (尤其是VDD/VSS等电源引脚)
     二、详细介绍VBAT:备份区供电电压,1.65V≤VBAT≤3.6V。 此引脚主要为以下模块供电:1.    RTC实时时钟2.    外部低速时钟振荡器3.    备份寄存器和备份SRAM 为了在VDD断电后(系统断电,设备断电等)保留备份寄存器的内容,可以将VBAT引脚连接到电池或其他......
  • STM32寄存器操作、模板构建
    2024年7月18日发布于博客园,本文涉及到STM32F4XX和STM32F1XX系列目录外设寄存器查找①名称②偏移地址③寄存器位表④位功能说明寄存器基本操作C语言的置位和清零具体方法设置GPIO流程给寄存器赋值带参数宏STM32F1xx芯片识别存储器映射寄存器映射让GPIOB端口的16个引脚输......