发帖数

53

原创数

53

关注者

11

阅读数

10236

点赞数

1

黄忠

  • 单片机学习之GPIO

    大家好,我是张飞实战电子黄忠老师,今天分享如何通过手册理解单片机IO知识点

    含义解释:

    1. GPIO:同我们常说的IO口一样, General Purpose Input Output (通用输入/输出)简称为GPIO,每个GPIO端口可通过软件分别配置成输入或输出模式。

    2. 外设:指的是除CPU以外的外围功能模块,只不过这部分电路依旧被封装在单片机内部,比如IOADCDACTIM等。

    3. 复位:把MCU恢复到最开始的状态,比如说我们把电脑重启了一次,就相当于复位了一次,在这里我们把MCU恢复到初始的状态称为复位。

    4. 往某一位写1,在硬件上就相当于把把它设置成高电平,清0则与之相反。

    芯片的缩略封装图:

    图片1.png 

    STM32F373CCT6 总共有48个引脚(图中左上角红色圈起来的1代表芯片的1号引脚,后面的以此类推,我们这里把1脚简称1Pin),分以下几个类别

    1.可以编程控制的引脚PAx(x表示012)PBx(x表示012)等以相同类似方式命名的。STM32F373CCT6分多组 IO口,分别用大写字母表示,即

    x=A/B/C/D/E/F,例如GPIOA,表示AIO口,这组IO口下面又有很多引脚,那么我们就用PA0PA1PA2等方式来表示,每组下面最多16IO口。通俗点来讲:GPIOA就相当于八年级五班这个班级,PA0PA1相当于班里的学生,有叫李刚的,有叫张的等等,每个班最多16个学生。

    我们看到有的可编程控制的引脚,例如PC14-OSC32-IN那么说明这个引脚有多种功能,可以当IO口用,也可以当做OSC32-IN用,在下面我们会具体解释这样的引脚。

    2. 不可编程控制的引脚1Pin(备用电源正脚),7 Pin(复位脚), 8 Pin(模拟电源负脚), 9 Pin(模拟电源/参考电压正脚), 17 Pin(数字电源正脚),23 PinSDADC1, SDADC2, SDADC3 地),24 PinSDADC1, SDADC2, SDADC3 电源)25 PinSDADC1, SDADC2, SDADC3的外部参考电压正),44 Pin(启动内存选择引脚),47 Pin(数字电源负脚),48 Pin(数字电源正脚)。

    1. 后备区域供电脚 VBAT 脚的供电采用 CR1220 纽扣电池和 VCC3.3 混合供电的方式,在有外部电源 (VCC3.3) 的时候, CR1220 不给 VBAT 供电, 而在外部电源断开的时候, 则由 CR1220给其供电。这样,VBAT 总是有电的,以保证 RTC 的走时以及后备寄存器的内容不丢失。

    2. BOOT0

    图片2.png 

    关于详细的引脚功能定义可以查阅《STM32F373xx》数据手册第33页,这里我们解释下关于引脚的功能问题:

    1. 默认功能:也就是引脚的普通功能。

    2. 复用功能:即将IO口用作普通输入输出以外的功能,通过配置相关寄存器后选择的功能,例如串口输入输出,使用时需要配置复用模式。

    想要配置成复用功能,首先需要查看引脚定义看看这个IO口可不可以被配成复用功能,这个是由IO的内部电路决定的。如果有才可以被配置,配置成复用功能不仅仅是要通过寄存器GPIOx_MODER配制成复用功能模式,而且还要通过GPIOx_AFRLGPIOx_AFRH寄存器选择IO复用功能。这样IO口才能真正被配成复用功能

    3. 附加功能:配置相关外设寄存器来选择的功能,比方配置ADC使能某些通道等来使能相应管脚的附加功能同样想要配置成附加功能,首先需要查看引脚定义看看这个IO口可不可以被配成附加功能,这个也是由IO的内部电路决定的。如果有,那么通过寄存器GPIOx_MODER配制成模拟功能模式

    每组通用 I/O 端口包括 4 32 位配置寄存器 (MODEROTYPEROSPEEDRPUPDR) 、2 32 位数据寄存器(IDR ODR) 、1 32 位置位/复位寄存器 (BSRR)1 32 位锁定寄存器 (LCKR) 2 32 位复用功能选择寄存器(AFRH AFRL)等,可以被配置成一下几种不同的模式:

    输入上拉IO口上拉就是在IO口通过接一个电阻到电源(注意这个电压要和单片机供电电压相同,否则过高会烧毁IO),电阻的大小决定了电源到IO口电流的不同,这就是我们常说的弱上拉等。下面附图一张。

    图片3.png 

     输入下拉:下拉就是在IO口通过接一个电阻到地,电阻的大小决定了IO口到地电流的不同,这就是我们常说的弱下拉等。下面附图一张。

    图片4.png 

    输入浮空/模拟输入:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。浮空最大的特点就是电压的不确定性,它可能是0V,也可能是VCC,还可能是介于两者之间的某个值浮空一般用来做ADC输入用,可能有的芯片把浮空模式和模拟输入模式分开了,在此解释一下,在浮空模式下使能了IO模拟功能就相当于是模拟输入

    图片5.png 

    开漏输出:开漏输出就是我们所说的OC输出,不输出电压,相当于N型三极管的集电极作为单片机的IO,需要在外部加一个上拉电阻配合使用。如图:

    图片6.png 

    推挽输出:可以输出高,低电平,但相对于普通的输出而言,这种输出方式增加了输出能力。如图:

     

    图片7.png 

    复用开漏输出、复用推挽输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。

    图片8.png 

    上图为引脚的内部电路框图(红圈内或旁边数字代表序号,下面简称1号等)

    输出部分解析:输出分三路

    第一路,1(/写动作-由片内外设控制)——>3号(经过一个逻辑门->输出控制电路)

    第二路,15号(写动作)——>14号(Bit Set/Reset register 位设置/清零寄存器),——>13号(Output data register数据输出寄存器)——>3号(经过一个逻辑门->输出控制电路)

    第三路,2号(复用功能输出)——>3号(经过一个逻辑门->输出控制电路)。

    三路都通过控制4号(MOS管电路,根据配置的不同模式,驱动P-MOS或者N-MOS或者两个一起驱动)——>5/7号的下拉/上拉电阻(我们可以看到上/下拉电阻有开关控制,意思就是可以通过外部的某些东西去控制使能或者失能上/下拉)——>6号的保护二极管(这里利用了二极管钳位的功能,可以在一部分程度上起到保护引脚的作用)——>IO口。

    输入操作解析:同样分三路

    第一路,IO——>6号的保护二极管输出到——>9号(模拟输入)——> 片上外设

    第二路,IO——>6号的保护二极管输出到——>8号(开关,可靠外部控制)——>10号(复用功能输入)——> 片上外设

    第三路,IO——>6号的保护二极管输出到——>8号(开关,可靠外部控制)——>12号(Input data register输入数据寄存器)——> 11号(可供读取数据)。

    如何结合寄存器以及硬件电路来实现具体输入输出请看下篇分析~


    收藏 0 回复 0 浏览 228
  • STM32学习GPIO之设计篇

    大家好,我是张飞实战电子黄忠老师,今日分享GPIO结合寄存器以及硬件电路,再来举例子分析输入输出。

    寄存器介绍:

    通过寄存器的位标注rw,我们可知这个寄存器的某个位是可读(r)并且可写的(w),我们也可以通过读寄存器里面的值得到引脚的配置信息,如果寄存器的位标注只有r或者w,那就代表这个寄存器的这个位只能进行读(r)或者写(w)

    复位后IO口寄存器的值每一位可能不都是一样的,也就是说复位后IO口的状态、工作模式等不完全相同,一定要注意。

    图片9.png 

    模式配置寄存器GPIOx_MODER,右上角用蓝色圆圈标识的地方告诉我们IO口的复位值,可以看到复位后对于AIO口来说初始值0XA8000000,对于BIO口初始值为0x00000280,其他组IO口都是0。该寄存器共 32 位,每 2个位控制 1IO

    如图中左下角划蓝线的为第0位和第1位。通过往第0位写0,第1位写0配置成Input Mode(输入模式),通过往第0位写0,第1位写1配置成General purpose output Mode(普通输出模式),通过往第0位写1,第1位写0配置成Alternate function Mode(复用功能模式),通过往第0位写1,第1位写1配置成Analog Mode(模拟模式)。

    例如我们要把这个寄存器第0位写1,其他都写0,表示如下:

    GPIOA->MODER |= 0x00000001;

    这就相当于把A组的PA0口配置成了普通输出模式,其他IO口配置成了输入模式。

    图片10.png 

    GPIOx_OTYPER寄存器用于控制 IO 的输出类型,仅用于输出模式,在输入模式下不起作用。该寄存器低 16 位(第0-15位)有效,每一个位控制一个 IO 口,根据往不同的位写1(开漏输出)/0(推挽输出)来配置成不同的输出模式。复位后,该寄存器值均为 0,即默认为推挽输出。

    例如我们往AIO口的第0位写1那就将PA0成开漏输出模式:

    GPIOA->OTYPER |= 0x00000001;

    图片11.png 

    GPIOx_OSPEEDR寄存器用于控制 IO的输出速度,仅用于输出模式,在输入模式下不起作用。该寄存器每 2 个位控制一个 IO 口,根据往不同的位写1/0不同的位来配置成不同的模式。复位后对于A口来说初始值为0X0C000000,对于B口初始值0x000000C0,其他口都是0

    例如我们往AIO口的第0位和第1位写1那就将PA0成高速输出模式:

    GPIOA->OSPEEDR |= 0x00000003;

    GPIOx_PUPDR寄存器用于控制 IO 的上拉/下拉,该寄存器每 2 个位控制一个 IO 口, 用于设置上下拉,根据往不同的位写1/0不同的位来配置成不同的模式。复位后对于A口来说初始值为0X64000000,对于B口初始值0x00000100,其他口都0x0C000000

    例如我们往AIO口的第0位写1那就将PA0下拉模式:

    GPIOA->PUPDR |= 0x00000001;

    GPIOx_ODR寄存器是IO口的输出数据寄存器,寄存器低 16 位(第0-15位)有效,寄存器每 1 个位控制一个 IO 口, 也就是这个里面位的状态代表了单片机输出的状态,根据往不同的位写1/0不同的位来配置成不同的模式。复位后都是0

    例如我们往AIO口的第0位写1PA0就输出高:

    GPIOA->0DR |= 0x00000001;

    对于一些其他的寄存器大家可以去参考数据手册,按照上面讲述的方法去配置。

    硬件设计:

     

    图片12.png 

    图示LED灯部分我们用到了B组的2PIN(脚的简称)和4PIN, C组的15PIN, D组的8PIN, E组的8PIN9PIN

    LED灯硬件的设计决定了LED灯的驱动方法,LED灯相当于一个发光二极管,如果单片机引脚输出高,则二极管的左边和右边电压相同,不亮,反之单片机输出低,右边电压大于左边电压通过单片机内部电路形成回路点亮二极管,那麽通过控制引脚输出的高低就控制了LED灯的亮灭。

    软件设计:

    下面就通过实际的寄存器来配置IO口模块,下面为LED寄存器配置代码:

    void Init_GPIO(void)

    {

    //开启IO时钟

    /**********************LED*****************************/

    RCC->AHBENR |= (1<<18);  //I/O port C clock enabled

    RCC->AHBENR |= (1<<19);  //I/O port D clock enabled

    RCC->AHBENR |= (1<<20);  //I/O port B clock enabled

    RCC->AHBENR |= (1<<17);  //I/O port A clock enabled

    RCC->AHBENR |= (1<<21);  //I/O port E clock enabled

    GPIOB->MODER = 0; //Reset register

        GPIOB->OSPEEDR = 0;//Reset register

    GPIOB->PUPDR = 0;//Reset register

    //GPIOB_Pin2  GPIOB_Pin4

    GPIOB->MODER |= 0x00000110; //设置GPIOB_Pin2/Pin4输出模式

       GPIOB->OSPEEDR |= 0x00000330;//设置GPIOB_Pin2/Pin4输出速度

    GPIOB->ODR |= 0x0014;    //设置GPIOB_Pin2/Pin4输出高电平

    //GPIOE_Pin8 GPIOE_Pin9

    GPIOE->MODER |= 0x00050000; //设置GPIOE_Pin8/Pin9输出模式

       GPIOE->OSPEEDR |= 0x000F0000; //设置GPIOE_Pin8/Pin9输出速度

    GPIOE->ODR |= 0x0300; //设置GPIOE_Pin8/Pin9输出高电平

    //GPIOD_Pin8

    GPIOD->MODER |= 0x00010000; //设置GPIOD_Pin8输出模式

       GPIOD->OSPEEDR |= 0x00030000; //设置GPIOD_Pin8输出速度

    GPIOD->ODR |= 0x0100; //设置GPIOD_Pin8输出高电平  

    //GPIOC_Pin15

    GPIOC->MODER |= 0x40000000; //设置GPIOC_Pin15输出模式

       GPIOC->OSPEEDR |= 0xC0000000; //设置GPIOC_Pin15输出速度

    GPIOC->ODR |= 0x8000; //设置GPIOC_Pin15输出高电平

    }

    上面没有配置的寄存器我们使用默认值,通过改变输出数据寄存器(ODR)的值,控制某一个引脚输出高和低控制LED灯的状态

    例如:控制IO输出高电平:

    GPIOD->ODR |= 0x0100;    //设置GPIOD_Pin8输出高电平

    控制IO输出低电平:

    GPIOD->ODR &= 0xFEFF;    //设置GPIOD_Pin8输出低电平

    在这里我们仅配置IO口的推挽高速输出,不上拉,不下拉模式作为演示,大家也可以根据上面介绍的方法去配置调试其他模式,GPIO部分设计实现介绍就在此结束,若有不理解的欢迎交流~


    收藏 0 回复 0 浏览 210
  • 啥?烧录器连不上STM32单片机了,别慌,自举模式来帮忙!

    大家好,我是张飞实战黄忠老师,今天我们来分享STM32单片机的自举BOOT模式。

    当你拿到项目的线路板,打开电脑,噼里啪啦一阵子,撸了一段代码出来,往单片机里面一下载,纳尼?突然发现烧录器掉线了,怎么整都连接不上了,这个时候整个人心情都不好了,别慌,恢复心情,你需要用到下面这个高端大气上档次的技术,首先说明一下我们本次文章参考芯片以SMT32F103C8T6来做说明,其他芯片同理,话不多说,我们开整。

     

    首先我来简单阐述一下这种方法的原理,这种方法是利用了STM32单片机的“自举BOOT模式”,首先使单片机处于系统BOOT模式,也就是让单片机启动的时候从System memory启动,然后在PC机操作上位机软件通过串口发送控制命令擦除芯片中存储的程序。

     

    1.首先我们要知道第一个信息,有的单片机有很多个串口,那到底是从哪一个串口来发送这个命令呢?我们从数据手册的“存储器和总线架构”章节(2.4启动配置小节)获取如下信息,可以得出可以使用USART1接口启动自举程序。具体要发送什么命令,可以参考AN2606手册,我已经通过这个手册提炼出指令信息,编写成一个上位机小工具。

    图片13.png                           

    2. 那么如何进入系统自举模式呢?从下图画红线处可以得出信息,想要进入系统自举模式,需配置BOOT0引脚为1(高电平),BOOT1引脚为0(低电平),然后复位单片机,那么在SYSCLK的第4个时钟上升沿会锁定BOOT引脚状态,并选择启动模式为系统存储器,即系统自举模式。

        图片14.png

                                    

    3. 接下来我们看看接线图,我画出了简单的示意图如下,各位看官,请结合下图看具体操作方法:

    图片15.png 

                                3

    1).USB转换工具按照图示方法连接(注意全过程不需要使用烧录器且此步操作后目标板已经带电,如果目标板3.3V功耗很大,需要给目标板用外部电源供电)。

    2).在设备管理器中查看此转换工具对应的串口号(注意如果没有识别到串口,需要安       装驱动。)识别到的结果如下图4所示:

    3).设置BOOT0引脚为高电平,BOOT1引脚为低电平(如果有的MCU没有BOOT1引脚可以忽略),并复位单片机(可以通过单片机复位引脚来复位单片机),使单片机处于自举BOOT模式。

     图片16.png  图片17.png

      

    4).在上位机小工具中选择对应的COM串口号,点击打开串口

    5).点击擦除按钮

    6).在小工具的中间窗口处会显示擦除成功

    经过上面的6步擦除成功后,把目标板恢复至初始状态,即可继续正常使用了。(注意在连接不上之前,需先检查烧录器和MCU的接线是否接好)。

    如果需要上述小工具的,可以在公众号中添加客服二维码,备注领取串口小工具,来索取。


    收藏 0 回复 0 浏览 108
  • 图解边沿对齐,中心对齐PWM....

    大家好,我是张飞实战电子黄忠老师,今天我们来讲解下图解边沿对齐,中心对齐PWM...

    在说边沿对齐,中心对齐前,我们先来段铺垫,PWM又称脉冲宽度调制,我们通过调节脉冲的占空比,我们可以控制电压的大小(比如我们满占空比时电压为12V,我们可以通过调节占空比让电压变为7V5V甚至变为0V,实现输出电压可控)

    调节占空比后,输出电压怎么就变化了呢?可以用等效面积法来解释,例如在1ms周期里,满占空比时输出电压为12V50%占空比时(即高低电平各占时间为0.5ms)高电平在整个周期的面积只有原来的1/2了,此时输出电压就等效为12*1/2=6V,那么通过调节不同的占空比,也就实现了输出电压调节。如图:

    图片18.png 

    STM32中是怎么生成PWM波的呢?时钟是芯片的心脏,没有时钟,芯片就是一块“废物”,有了时钟,芯片才能有条不紊的工作,那时钟跟我们要讲的PWM有什么关系呢?请看下图,STM32内部的定时器框图,看看它是如何生成PWM的。

    图片19.png 

    方框内部的CNT Counter计数器会根据输入的时钟沿跳变来进行递加/减,时钟的频率决定了计数器递加/减的频率,这个计数器的值同时会和Auto-reload register(控制周期)、Capture/Compare x register(控制占空比)进行比较,当与控制占空比的寄存器值发生匹配时则控制输出引脚TIMx_CHx发生电平反转,当与控制周期寄存器值发生匹配时,周期结束,引脚电平置位,再次重复如上动作,就在引脚上输出了变化不同的电平,这个就是我们需要的PWM

    这个定时器模块可以根据软件编程设置出不同的PWM模式,定时器内部CNT Counter可被编程为向上、向下、向上向下运行,我们说的边沿对齐,和中心对齐就要从这个计数方式上切入,下面我们先来看三种不同的计数方式。

    1.CNT被设置为向上计数时,计数器从0递增向上计数到自动重载值(Auto-reload register),然后计数器又回到0,重新开始。

    图片20.png图片21.png 

    2.CNT被设置向下计数时,计数器从自动重载值递减向下计数,计数到0,计数器又回到重载值,重新开始。

    图片22.png图片23.png 

    3.CNT被设置向上向下计数时,计数器从0递增向上计数到自动重载值,然后计数器从自动重载值递减向下计数,计数到0然后又开始递增向上计数。

    图片24.png图片25.png 

    那这三种模式和2PWM又是什么关系呢?PWM是怎么从引脚上输出的呢?请看下图:

    1.向上/下计数模式PWM生成(只展示出了向上计数,向下计数同理):

    图片28.png图片29.png 

    2.向上向下计数模式PWM生成:

    图片26.png图片27.png 

    上文中提到的向上计数/向下计数,这两种生成PWM的方式,我们通常称为边沿对齐PWM;既向上又向下这种生成PWM的方式,我们称为中心对齐PWM当然,发生匹配的时候引脚电平如何变化,是变高还是变低,这个可以通过软件编程来设置。

    通过PWM调节输出电压,比如可以控制做呼吸灯,也可以实现电机的调速,不同的调速算法,会用到不同的PWM等等。


    收藏 0 回复 0 浏览 483
  • 带你在单片机编程中熟练使用const

    C语言关键字中const举足轻重,我们今天就深度聊一聊const的定义和实际应用,让它不再是迷。

    C语言中const关键字是constant的缩写,是恒定不变的意思。通常翻译为常量、常数等,我们一看到const关键字马上就想到了常量。这是不精确的,精确来说应该是只读变量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。那么const推出的初始目的正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

    事实上在C语言中const功能很强大,它可以修饰变量、数组、指针、函数参数等。

    1const 修饰的只读变量

    C语言中采用const修饰变量,功能是对变量声明为只读特性,并保护变量值以防被修改。

    例如:

    const  int Max = 100;

    int  Array[Max]

    这个大家可以在Visual C++6.0创建一个.c文件测试一下,你会发现在.c文件中编译器会提示出错。我们知道定义一个数组必须指定其元素的个数,这也从侧面证实在C语言中const修饰的Max仍然是变量,只不过是只读属性罢了。

    还有值得注意的是,定义变量的同时,必须初始化,并且不能再重新赋值。

    2节省空间,避免不必要的内存分配,同时提高效率

    编译器通常不为普通const只读变量分配存储空间,而是将他们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。

    例如:

    #define  M  3   //宏常量

    const int N= 5;   //此时并未将N放入内存中

    int i = N;     //此时为N分配内存,以后不再分配

    int I = M;     //预编译期间进行宏替换,分配内存

    int j = N;     //没有内存分配

    int J = M;    //再进行宏替换,又一次分配内存

    const定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数。所以,const定义的只读变量在程序运行过程中只有一份备份(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个备份。#define宏是在预编译阶段进行替换,而const修饰的只读变量是在编译的时候确定其值。#define宏没有类型,而const修饰的只读变量具有特定的类型。

    3、修饰一般变量

    一般变量是指简单类型的只读变量。这种只读变量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后,例如:

    int  const  i = 2;     const  int  i  =  2;

    4 修饰数组

    C语言中const还可以修饰数组,举例如下:

    const int array[5] = {1,2,3,4,5};

    array[0] = array[0]+1; //错误

    数组元素与变量类似,具有只读属性,不能被更改;一旦更改,如程序将会报错。

    5 修饰指针

    C语言中const修饰指针要特别注意,共有两种形式,一种是用来限定指向空间的值不能修改;另一种是限定指针不可更改。举例说明如下:

    Const离谁近修饰谁的原则,

    例如:

    const int * p1; //定义1,p1可变,p1指向的对象不可变

    int * const p2; //定义2,p2不可变,p2指向的对象可变

    上面定义了两个指针p1p2

    在定义1const限定的是*p1,即其指向空间的值不可改变,若改变其指向空间的值如*p1=20,则程序会报错;但p1的值是可以改变的,对p1重新赋值如p1=&k是没有任何问题的。

    在定义2const限定的是指针p2,若改变p2的值如p2=&k,程序将会报错;但*p2,即其所指向空间的值可以改变,如*p2=80是没有问题的,程序正常执行。

    6修饰函数参数

    const修饰符也可以修饰函数的参数,当不希望这个参数值在函数体内被意外改变时使用。所限定的函数参数可以是普通变量,也可以是指针变量。举例如下:

    void fun1(const int i)

    {

    其它语句

    ……

    i++; //i的值进行了修改,程序报错

    其它语句

    }

    告诉编译器i在函数体中不能改变,从而防止了使用者的一些无意或者错误的修改。

    void fun2(const int *p)

    {

    其它语句

    ……

    (*p)++; //p指向空间的值进行了修改,程序报错

    其它语句

    }

    7修饰函数的返回值

    Const修饰符也可以修饰函数的返回值,返回值不可被改变。

    Const int Fun(void);

    到这里const的定义和常用的说明我给大家做了描述,有疑问和其他想法欢迎交流~


    收藏 0 回复 0 浏览 234
×
黄忠