江协科技STM32学习笔记

2024/1/21

一.模块化编程      

        1.概述

        就是像一些功能如:LED灯的点亮,蜂鸣器的启动是需要在代码中经常要用到的。但是如果每次都要在程序中进行复写一遍的话就会十分的麻烦,所以将这些代码进行封装再调用的话代码就会变得十分的方便快捷。把各个模块的代码分别放在.c文件中,在.h文件存放函数外部可调用函数声明,在其他的.c文件想要使用其中的代码时,只需要#include"XXX.h"文件即可

预编译往往以#号开头,在真正编译前对代码进行一些处理。

2.具体实现

1.h文件 

#ifndef __XXXX_X    //X一般后面为大写的文件名,此段意思为如果没有定义的话则执行下面语句

#define __XXXX_X   //定义XXXX

#endif //与ifndef对应

2.c文件

        一般写具体的函数实现。

二.中断

1.概述

        中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行 

2.具体实现

 基本流程为

1.配置RCC时钟,启用相应的GPIO和AFIO。

EXTI与NVIC的时钟不需要RCC时钟开启

   示例代码:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

2.配置GPIO口

    GPIO_InitTypeDef GPIO_InitSturcture;
    GPIO_InitSturcture.GPIO_Mode=GPIO_Mode_IPD;
    GPIO_InitSturcture.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitSturcture.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitSturcture);

3.配置AFIO

AFIO为一个数据选择器,主要是在前面n个GPIO口中挑选一个到EXTI程序中。其代码中为AFIO这四个字母,但是要知道这是配置AFIO的。在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//为AFIO的初始化程序
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//为AFIO的初始化程序

 GPIO_EXTILineConfig功能为配置AFIO的数据选择器来配置我们想要的中断引脚。

上面代码为配置GPIOB  0号和1号口 

4.配置EXTI

EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序

支持的触发方式:上升沿/下降沿/双边沿/软件触发

支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(因为AFIO与EXTI的接口只有十六个,AFIO只能从众多GPIO的接口中挑选一个来接入EXTI,所以像PA0,PB0,PC0等都是不行的)

通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒

触发响应方式:中断响应/事件响应

    EXTI_InitTypeDef EXTI_InitStucture;
    EXTI_InitStucture.EXTI_Line=EXTI_Line0 | EXTI_Line1;//与所选pin口有关,此为第十四个线路
    EXTI_InitStucture.EXTI_LineCmd=ENABLE;//开启中断
    EXTI_InitStucture.EXTI_Mode=EXTI_Mode_Interrupt;//中断触发模式
    EXTI_InitStucture.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&EXTI_InitStucture);
    //配置好后进行初始化,这样PB14的电平信号就能通过EXTI通向下一级NVIC                

    

5.配置NVIC

        NVIC主要进行中断分配,NVIC只有一个输出口,根据中断的优先级来告诉CPU来先进行那个中断处理 

 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
    NVIC_Init(&NVIC_InitStructure);
}

这里可以看到上面那个配置过的结构体在用过后可以在下面直接用,不用再重新定义。

3.注意事项

在stm32中中断函数是固定的,每个中断通道对应一个中断函数,如果写错了中断函数的名字就会进不了中断。

                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10

其中有一些是共用的,所以要进行中断标志位的判断,确保是我们想要的中断源进入中断函数。

列如:EXTI_GetFlagStatus(EXTI_Line14)==SET,判断是不是EXTI14进入了中断标志位。

在中断结束后一定要清除中断标志位,不然中断标志位置1后会一直申请中断。

 void EXTI15_10_IRQHandler(void)
 {
   if(EXTI_GetFlagStatus(EXTI_Line14)==SET)
   {
    Delay_ms(30);
    sum++;


    EXTI_ClearITPendingBit(EXTI_Line14);//清除中断标志位,防止死循环
   }
 }

三.GPIO

1.GPIO的输入模式

1、上拉输入:IO口在无输入的情况下,保持高电平。

2、下拉输入:IO口在无输入的情况下,保持低电平。

3、浮空输入:浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的。

4、模拟输入:输入信号不经施密特触发器直接接入,输入信号为模拟量而非数字量,其余输入方式输入数字量。

        在设置按键的输入模式时由于按键接的是地,所以在按键的GPIO口设置为上拉输入,当按下按键时使GPIO口接地输入低电平。 

2.GPIO的读取函数

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

读取输入数据寄存器某一个端口的输入值,读取按键需要用到此函数

uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

读取整个输入数据寄存器

uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

读取输出数据寄存器,一般看自己输出的是什么。

uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

读取整个输出数据寄存器

3.外设

        像输入外设一般近似于按键,所以在GPIO口的模式设置中可以与按键的模式一致,如光敏传感器,而输出可以像LED,如蜂鸣器。

1.OLED基本函数

OLED显示屏一共有六行十六列字符框

四.TIM定时器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

在开启时钟时要选择正确的总线,如通用定时器总线为APB1,则时钟开启函数为RCC_APB1PeriphClockCmd

1.预分频器

        实际分频系数=预分频器的值+1,最大值可以设置为65535,即65536次分频。

2.计数器

        计数器为十六位的,可以从0加到65535,计数时钟每来一个上升沿计数器就加1。当加到目标值就会产生中断。在通用中还可从目标值向下自减。

3.自动重装寄存器

        主要是存储要计数的目标,当计数器的计数值等于重装值时,就是计时时间到了,会产生一个中断信号。并且清零计数器。

只需要将上面的每个模块都打通(即完成每个模块的初始化)就可以让定时器工作了。

4.相关函数

4.1 时基单元
TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

用来配置时基单元

TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

把结构体变量赋一个默认值

4.2 运行控制

TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

使能计数器,对应上图中的运行控制

4.3 中断输出控制

TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

使能中断输出信号

4.4 时钟源选择

TIM_InternalClockConfig(TIM_TypeDef* TIMx);

选择内部时钟

TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

选择ITRX其他定时器时钟

TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);

选择Tlx具体的某个引脚

TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,int16_t ExtTRGFilter);

选择ETR通过外部时钟模式1输入的时钟,还有TIM_ETRClockMode2Config外部触发输入,TIM_ETRClockMode1Config为外部输入

void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);

单独配置ETR引脚的预分频器,极性,滤波器这些参数

5.时钟

内部时钟:是可以自己进行计数跳变的,不需要其他触发条件,设置好后自动进行数字增加。

外部时钟:当接入外部时钟时,计数器接受外部的脉冲信号,列如当外部时钟为按键时,只有在按下按键时时钟才会进行增加。

五.TIM输出比较

1.OC(Output Compare)输出比较

输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形

有效电平=高电平 无效电平=低电平

2.PWM(Pulse Width Modulation)脉冲宽度调制

在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域

PWM参数:      

频率 = 1 / TS           占空比 = TON / TS  (高电平时间比总时间)        分辨率 = 占空比变化步距

黄线:ARR         蓝线:CNTDSZ ES        W         红线:CCR        绿线:电平

PWM频率:    Freq = CK_PSC / (PSC + 1) / (ARR + 1)

PWM占空比:    Duty = CCR / (ARR + 1)

PWM分辨率:    Reso = 1 / (ARR + 1)

3.相关函数

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

用来配置输出比较模块(O=output C=Compare),由于存在复用所以OC1只能对应PA0,就是说使用OC1使只能使用PA0输出,OC2只能使用PA1输出,OC3对应PA2,OC4对应PA3

void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

给输出比较模块赋一个默认值,因为在进行结构体配置是不一定会用到所有的结构体,就意味着有些结构体不会被配置处于不确定状态,有可能会导致一些错误,而这个函数就是直接先把所有的结构体赋值防止出现错误。

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);

单独更改CCR的值。

void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);

void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);

void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

单独更改CCR寄存器的函数,在运行时更改占空比。

在配置好PWM后使用到GPIO口用来输出配置好的GPIO信号,所以GPIO也要进行初始化。

4.TIM输入捕获

IC(Input Capture)输入捕获

输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数

每个高级定时器和通用定时器都拥有4个输入捕获通道

可配置为PWMI模式,同时测量频率和占空比

可配合主从触发模式,实现硬件全自动测量  

5.TIM编码器接口

编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度

两个输入引脚借用了输入捕获的通道1和通道2

降低软件资源的浪费,使硬件进行自增或自减

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值