stm32基本外设使用总结

stm32基本外设使用总结

一、GPIO

二、串口

三、ADC和DMA

四、TIM定时器

五、bootloader升级

一、GPIO

8种工作模式:

浮空输入:上拉电阻和下拉电阻都断开,引脚电平完全由外部状态决定;

上拉输入:内部上拉电阻接VDD,外部引脚没输入时,默认高电平,外部引脚输入低电压时,呈现低电平;

下拉输入:内部下拉电阻接VSS,外部引脚没输入时,默认低电平,外部引脚接高电压时,呈现高电平;

模拟输入:TTL肖特基触发器断开,外部输入直接通到内部,比如ADC模块;

通用开漏输出:保持P-MOS断开,向引脚写0,下面的N-MOS管导通,对外输出低电平;向引脚写1,下面的N-MOS管断开,此时IO引脚悬空,IO引脚电流为0,无论外部加多大的电压,电流都为0,对外呈现高阻态;

通用推挽输出:向引脚写0,下面的N-MOS管导通,上面的P-MOS管断开,对外输出低电平;向引脚写1,上面的P-MOS管导通,下面的N-MOS管断开,对外输出高电平;

复用开漏输出:引脚给其他模块控制,控制属性和通用开漏输出一样;

复用推挽输出:引脚给其他模块控制,控制属性和通用推挽输出一样;

复用:将IO引脚交给芯片的其他模块控制;通用:直接写寄存器控制IO引脚;

将一个GPIO配置为中断引脚的代码如下:

/* 1、使能NVIC中断控制器的中断通道 */

/* 定义一个 NVIC 结构体 */

NVIC_InitTypeDef nvic_initstruct = {0};

/* 开启 AFIO 相关的时钟 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

/* 配置中断源 */

nvic_initstruct.NVIC_IRQChannel = EXTI0_IRQn;

/* 配置抢占优先级 */

nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 1;

/* 配置子优先级 */

nvic_initstruct.NVIC_IRQChannelSubPriority = 0;

/* 使能配置中断通道 */

nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&nvic_initstruct);

/* 配置引脚为浮空输入状态 */

/* 定义一个 GPIO 结构体 */

GPIO_InitTypeDef gpio_initstruct = {0};

开启 KEY 相关的GPIO外设/端口时钟 */

RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK_PORT,ENABLE);

/* IO输出状态初始化控制 */

GPIO_SetBits(KEY1_GPIO_PORT,KEY1_GPIO_PIN);

/*选择要控制的GPIO引脚、设置GPIO模式为 浮空输入、设置GPIO速率为50MHz*/

gpio_initstruct.GPIO_Pin = KEY1_GPIO_PIN;

gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(KEY1_GPIO_PORT,&gpio_initstruct);

/* 配置GPIO到NVIC的中断线,包括触发模式和使能 */

/* 定义一个 EXTI 结构体 */

EXTI_InitTypeDef exti_initstruct = {0};

/* 开启 AFIO 相关的时钟 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

/* 选择中断信号源*/ GPIO_EXTILineConfig(KEY1_EXTI_PORTSOURCE,KEY1_EXTI_PINSOURCE);

/* 选择中断LINE */

exti_initstruct.EXTI_Line = EXTI_Line0;

/* 选择中断模式*/

exti_initstruct.EXTI_Mode = EXTI_Mode_Interrupt;

/* 选择触发方式*/

exti_initstruct.EXTI_Trigger = EXTI_Trigger_Falling;

/* 使能中断*/

exti_initstruct.EXTI_LineCmd = ENABLE;

EXTI_Init(&exti_initstruct);

二、串口

串口是一种异步串行通信接口,配置主要包括引脚模式配置、数据帧格式配置、波特率配置、中断配置

/* 配置nvic使能串口的中断 */

/* 定义一个 NVIC 结构体 */

NVIC_InitTypeDef nvic_initstruct = {0};

/* 开启 AFIO 相关的时钟 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

/* 配置中断源 */

nvic_initstruct.NVIC_IRQChannel = DEBUG_IRQ;

/* 配置抢占优先级 */

nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 1;

/* 配置子优先级 */

nvic_initstruct.NVIC_IRQChannelSubPriority = 0;

/* 使能配置中断通道 */

nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&nvic_initstruct);

/* 配置串口的波特率和数据帧格式 */

/* 定义一个 USART 结构体 */

USART_InitTypeDef usart_initstruct = {0};

/* 开启 DEBUG 相关的GPIO外设/端口时钟 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

/* 配置串口的工作参数 */

usart_initstruct.USART_BaudRate = 115200;

usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

usart_initstruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;

usart_initstruct.USART_Parity = USART_Parity_No;

usart_initstruct.USART_StopBits = USART_StopBits_1;

usart_initstruct.USART_WordLength = USART_WordLength_8b;

USART_Init(USART1,&usart_initstruct);

USART_ITConfig(DEBUG_USARTX,USART_IT_RXNE,ENABLE);//开启串口数据接收中断

/* 将两个引脚分别配置为复用推挽输出和上拉输入模式 */

/* 定义一个 GPIO 结构体 */

GPIO_InitTypeDef gpio_initstruct = {0};

/* 开启 DEBUG 相关的GPIO外设/端口时钟 */ RCC_APB2PeriphClockCmd(DEBUG_TX_GPIO_CLK_PORT,ENABLE);

/*选择要控制的GPIO引脚、设置GPIO模式为 推挽复用、设置GPIO速率为50MHz*/

gpio_initstruct.GPIO_Pin = DEBUG_TX_GPIO_PIN;

gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;

gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(DEBUG_TX_GPIO_PORT,&gpio_initstruct);

/* 开启 DEBUG 相关的GPIO外设/端口时钟 */

RCC_APB2PeriphClockCmd(DEBUG_RX_GPIO_CLK_PORT,ENABLE);

/*选择要控制的GPIO引脚、设置GPIO模式为 上拉输入/浮空输入、设置GPIO速率为50MHz*/

gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;

gpio_initstruct.GPIO_Pin = DEBUG_RX_GPIO_PIN;

gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(DEBUG_RX_GPIO_PORT,&gpio_initstruct);

/* 使能串口 */

USART_Cmd(USART1,ENABLE);

三、ADC和DMA

使用ADC1采样GPIO的模拟输入,ADC1只使用单通道,独立模式;使用软件触发ADC转换的模式,转换完成后,ADC1会触发DMA请求,DMA将转换结果搬移到SRAM;软件周期性的触发ADC转换;

/* 配置ADC1为独立单通道模式,取消连续采样,ADC时钟设置为PLL的8分频,也就是9MB,每次采用周期设置为最大239个周期 */

/* ADC控制器初始化 */

/* 定义一个ADC结构体 */

ADC_InitTypeDef adc_initstruct = {0};

/* 开启ADC相关的GPIO外设/端口时钟 */

ADCX_APBXCLKCMD(ADCX_CLK_PORT, ENABLE);

/* ADC 模式配置 */

adc_initstruct.ADC_Mode = ADC_Mode_Independent; //只有一个ADC,属于独立模式

adc_initstruct.ADC_ScanConvMode = ENABLE; //开启扫描模式,单通道不需要

adc_initstruct.ADC_ContinuousConvMode = DISABLE; //取消连续扫描模式

adc_initstruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不需要外部触发转换,使用软件开启

adc_initstruct.ADC_DataAlign = ADC_DataAlign_Right; //转换结构右对齐

adc_initstruct.ADC_NbrOfChannel = ADCX_CHANNEL_NUM;

ADC_Init(ADCX,&adc_initstruct); //初始化ADC

/* ADC的转化配置 */

RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC的时钟为PLL2的8分频,9MHz

ADC_RegularChannelConfig(ADCX,POTENTIOMETER_ADC_CHANNEL,1,ADC_SampleTime_239Cycles5); //配置ADC通道的采样顺序和时间

ADC_Cmd(ADCX,ENABLE); //开启ADC转换

/* 进行校准 */

ADC_ResetCalibration(ADCX); //选择需要校准的ADC初始化

while(ADC_GetResetCalibrationStatus(ADCX)); //等待校准初始化完成

ADC_StartCalibration(ADCX); //开始校准

while(ADC_GetCalibrationStatus(ADCX)); //等待校准完成

/* 配置DMA从ADC1转换结果寄存器到SRAM搬移数据 */

/* 定义一个DMA结构体 */

DMA_InitTypeDef dma_initstructure = {0};

/*开启ADC_DMA相关的DMA外设/端口时钟*/

RCC_AHBPeriphClockCmd(ADCX_DMA_CLK_PORT,ENABLE);

/*复位DMA控制器*/

DMA_DeInit(ADCX_DMA_CHANNEL);

dma_initstructure.DMA_PeripheralBaseAddr = ADC1_DR_ADDRESS; //外设基地址

dma_initstructure.DMA_MemoryBaseAddr = (uint32_t)&adc_source_convertedvalue; //AD转换值所存放的内存基地址

dma_initstructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为数据传输的来源

dma_initstructure.DMA_BufferSize = ADCX_CHANNEL_NUM; //定义指定DMA通道 DMA缓存的大小

dma_initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变

dma_initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增

dma_initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据位宽16

dma_initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据位宽16

dma_initstructure.DMA_Mode = DMA_Mode_Normal; //工作模式

dma_initstructure.DMA_Priority = DMA_Priority_High; //高优先级

dma_initstructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存

DMA_Init(ADCX_DMA_CHANNEL,&dma_initstructure); //初始化ADC

DMA_ITConfig(ADCX_DMA_CHANNEL,DMA_IT_TC,ENABLE); //使能注入转换完成中断,用于读取转换值

/* GPIO设置为模拟输入 */

/* 定义一个GPIO结构体 */

GPIO_InitTypeDef gpio_initstruct = {0};

/* 开启POTENTIOMETER相关的GPIO外设/端口时钟 */ RCC_APB2PeriphClockCmd(POTENTIOMETER_SIG_GPIO_CLK_PORT,ENABLE);

/*选择要控制的GPIO引脚、设置GPIO模式为模拟输入、设置GPIO速率为50MHz*/

gpio_initstruct.GPIO_Mode = GPIO_Mode_AIN;

gpio_initstruct.GPIO_Pin = POTENTIOMETER_SIG_GPIO_PIN;

gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(POTENTIOMETER_SIG_GPIO_PORT,&gpio_initstruct);

/* NVIC使能DMA中断 */

/* 定义一个中断控制器结构体 */

NVIC_InitTypeDef nvic_initstructure;

// 配置中断优先级

nvic_initstructure.NVIC_IRQChannel = ADCX_INT_DMA_IRQ;

nvic_initstructure.NVIC_IRQChannelPreemptionPriority = 1;

nvic_initstructure.NVIC_IRQChannelSubPriority = 0;

nvic_initstructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&nvic_initstructure);

/* FreeRTOS创建一个任务周期性开启ADC转换 */

DMA_Cmd(ADCX_DMA_CHANNEL,ENABLE); //使能DMA

ADC_DMACmd(ADCX,ENABLE); //使能DMA请求

ADC_SoftwareStartConvCmd(ADCX,ENABLE); //由于没有采用外部触发,所以配置软件触发ADC转换

/* 转换完成,触发DMA完成数据搬移后,触发DMA中断,在中断里面设置ADC转换停止,停止DMA请求,重新设置每次DMA传输的数据量,清除DMA中断标志位 */

ADC_SoftwareStartConvCmd(ADCX,DISABLE); //由于没有采用外部触发,所以配置软件触发ADC转换

DMA_Cmd(ADCX_DMA_CHANNEL,DISABLE); //使能DMA

ADC_DMACmd(ADCX,DISABLE); //使能DMA请求

DMA_SetCurrDataCounter(ADCX_DMA_CHANNEL,ADCX_CHANNEL_NUM);//重新设置DMA传输计数值,必须在DMA失能下进行

DMA_ClearITPendingBit(DMA1_IT_TC1);

四、TIM定时器

配置TIM3_CH2输出PWM波形,对应GPIO引脚是PB5;配置TIM4_CH3为输入捕获模式,对应GPIO引脚是PB8,将PB5输出的PWM波形连接到PB8,那么就可以用TIM4_CH3测试PWM的周期;

/* 配置TIM3_CH2输出PWM波形 */

/* PB5引脚配置重映射到TIM3_CH2 */

/* 定义一个GPIO结构体 */

GPIO_InitTypeDef gpio_initstruct = {0};

/* 开启LED相关的GPIO外设/端口时钟 */

RCC_APB2PeriphClockCmd(W_LED_GPIO_CLK_PORT,ENABLE);

/* 开启重映射 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

/* 初始化io状态 */

GPIO_SetBits(W_LED_GPIO_PORT, W_LED_GPIO_PIN);

/*选择要控制的GPIO引脚、设置GPIO模式为 复用推挽输出、设置GPIO速率为50MHz*/

gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;

gpio_initstruct.GPIO_Pin = W_LED_GPIO_PIN;

gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(W_LED_GPIO_PORT,&gpio_initstruct);

/* 输入定时器的时钟是72MB,配置TIM3_CH2的预分频寄存器为72-1,重装载寄存器为1000-1,这样定时器的计数器可以计数1000次,周期是1ms;配置为PWM模式1,使能通道2 *、

/* 定义一个 GENERALTIM 结构体 */

TIM_TimeBaseInitTypeDef tim_timebaseinitstruct = {0};

/* 定义一个 PWM输出配置 结构体 */

TIM_OCInitTypeDef tim_ocinitstructure;

/* 开启 GENERALTIM 相关的GPIO外设/端口时钟 */

GENERAL_TIM_APBXCLKCMD(RCC_APB1Periph_TIM3,ENABLE);

/* 通用定时器配置 */

tim_timebaseinitstruct.TIM_Period = PWM_LED_PERIOD; // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断

tim_timebaseinitstruct.TIM_Prescaler = (72-1); // 设置预分频----1us

tim_timebaseinitstruct.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分频系数:不分频(这里用不到)

tim_timebaseinitstruct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式

// tim_timebaseinitstruct.TIM_RepetitionCounter = 0; //重复计数器的值,没用到不用管

TIM_TimeBaseInit(PWM_LED_TIM,&tim_timebaseinitstruct);// 初始化定时器

//例如向上计数时

//PWM模式1下,TIMx_CNT

// TIMx_CNT>TIMx_CCRn时,输出无效电平

//PWM模式2下,TIMx_CNT

// TIMx_CNT>TIMx_CCRn时,输出有效电平

/* PWM模式配置 */

tim_ocinitstructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1

tim_ocinitstructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出

tim_ocinitstructure.TIM_Pulse = PWM_LED_PULSE; //设置初始PWM脉冲宽度

tim_ocinitstructure.TIM_OCPolarity = TIM_OCPolarity_Low; //当定时器计数值小于CCR_Val时为低电平

//使能通道2和预装载

TIM_OC2Init(PWM_LED_TIM, &tim_ocinitstructure);

TIM_OC2PreloadConfig(PWM_LED_TIM, TIM_OCPreload_Enable);

TIM_ARRPreloadConfig(PWM_LED_TIM, ENABLE);//使能重载寄存器ARR

/* 使能 TIM */

TIM_Cmd(PWM_LED_TIM,ENABLE);

/* 周期性改变PWM的占空比 */

TIM_SetAutoreload(PWM_LED_TIM, pwm_cycle); //自动重装载寄存器

TIM_SetCompare2(PWM_LED_TIM, pwm_pulse); //CCR_Val寄存器,计数值小于这个就输出低电平,否则输出高电平

/* 配置TIM4_CH3为输入捕获模式,对应GPIO引脚是PB8 */

/* PB8设置为浮空输入模式 */

gpio_InitIntput(RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_8, GPIO_Mode_IN_FLOATING);

/* 打开TIM4的中断 */

NVIC_InitTypeDef NVIC_InitStructure;

/* Enable the TIM4 global Interrupt */

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

/* 设置TIM4的CH3为输入捕获模式 */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

TIM_ICInitTypeDef TIM_ICInitStructure;

TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;

TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;

TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;

TIM_ICInitStructure.TIM_ICFilter = 0x0;

TIM_ICInit(TIM4, &TIM_ICInitStructure);

/* TIM enable counter */

TIM_Cmd(TIM4, ENABLE);

/* Enable the CC3 Interrupt Request */

TIM_ITConfig(TIM4, TIM_IT_CC3, ENABLE);

/* 捕获到上升沿会触发TIM4定时器中断 */

void TIM4_IRQHandler(void)

五、bootloader升级

实现原理:把bootloader和应用程序APP分成两个工程实现,bootlaoder加载在FLASH起始地址0x08000000的地方,应用程序APP加载在FLASH的另外一个地方,比如0x08008000;bootloader通过跳转到应用程序的复位中断向量执行函数从而跳转到应用程序APP执行(复位中断向量函数在程序文件起始偏移4字节的地方,bootloader的复位向量地址是0x080000004,应用程序的复位向量地址是0x08008004);

/* Bootloader程序跳转应用程序的代码 */

#define APP_ADDR 0x08008000 //自定义的APP程序头地址

typedef void (*pFunction)(void); //函数指针,用来调用同一类型的函数

/*****************************************************************************

[函数名称]BootLoader_JumpToApp

[函数功能]BootLoader跳转到APP函数

[参 数]app_addr:APP程序入口地址

*****************************************************************************/

void BootLoader_JumpToApp (uint32_t app_addr)

{

pFunction jumo_to_application; //跳转函数指针,指向APP运行函数头地址后调用函数

uint32_t jump_address; //跳转地址变量

jump_address = *(__IO uint32_t*)(app_addr + 4); //计算APP运行函数头地址,为MSP主堆栈指针+4个地址偏移量

jumo_to_application = (pFunction)jump_address; //函数指针指向APP运行函数头地址

__set_MSP(*(__IO uint32_t*)app_addr); //设置主堆栈指针

jumo_to_application(); //跳转到APP应用程序,开始运行应用主程序

}

/* 调用跳转函数 */

BootLoader_JumpToApp(APP_ADDR);

应用程序设置:Keil工程设置FLASH的加载地址

应用程序main函数的入口处重新设置中断向量表的位置:SCB->VTOR = APP_ADDR;

参考资料:

<野火视频>

https://www.bilibili.com/video/BV1C4421Z7t8/?vd_source=eb04ac3759f85a5dd795269e17334fee&spm_id_from=333.788.videopod.episodes&p=25

<铁头山羊>

https://www.bilibili.com/video/BV1Sy41187Rt/?vd_source=eb04ac3759f85a5dd795269e17334fee&spm_id_from=333.788.player.switch

https://blog.csdn.net/2401_83606346/article/details/144164248

本文仅是为了加深自己的理解做个学习总结,还有其他博客没列出,如有侵权,请联系删除

相关星际资讯

蔸种的意思
365bet吧

蔸种的意思

🕒 12-13 👁️ 8653
禁书收录
365体育投注官网

禁书收录

🕒 08-15 👁️ 2671
喜欢骂人是什么心理?帮你分析了这6种原因!