历史上的今天
返回首页

历史上的今天

今天是:2025年02月09日(星期日)

正在发生

2020年02月09日 | 基于STM32单片机对跑马灯的控制

2020-02-09 来源:elecfans

基于学习STM32有一段时间了,特意写下一篇关于一个简单的跑马灯

的例程,梳理思路,也希望我自己的理解能帮到一些学习STM32的初学者

//======================================================//

首先,GPIO的配置种类有8种。分别为模拟输入、浮空输入,上拉输入、下拉输入、开漏输出、推挽输出、复用开漏输出、复用推挽输出。


下面将以实例的方式讲解GPIO的设置及实现过程。


事例1:跑马灯实验

跑马灯实验的功能:LED灯进行有规律闪烁。(下面的LED灯的数量都为2个,所以关于寄存器的配置也将以两组的方式进行讲解)


首先要知道普通的IO就两种功能一个为输入,一个为输出。然后再以输入和输出细分为以哪种模式输出、以哪种模式输入。要设置IO的模式这时就要使用到寄存器进行设置,STM32的IO端口一般由7个寄存器来进行控制。

基于STM32单片机对跑马灯的控制

分别为

(1)配置模式的2个32位的端口配置寄存器 CRL 和 CRH;

(2)2 个 32 位的数据寄存器 IDR 和 ODR;

(3)1 个 32 位的置位/复位寄存器BSRR;

(4)1个 16 位的复位寄存器 BRR;

(5)1 个 32 位的锁存寄存器 LCKR;

而我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、ODR。


首先,一般一个程序的开始都会从入口main函数开始执行,而一个功能的实现之前都需要做一些准备工作,当然跑马灯也不例外,在程序执行前,IO需要初始化,而初始化要做的就是对程序需要用到的GPIO的寄存器进行配置。然后设定输出设备的初始状态,即跑马灯的初始状态。一般程序在执行前,输出设备都会是处于关闭状态。最后增加延时函数,让LED灯在亮与灭之间有一段时间的状态保留,不然时间过快,人类的视觉根本捕抓它的状态变化。


下面来讲一下实现跑马灯效果的IO要怎么配置。首先先说一下使用LED来进行跑马功能的话只需要用到RCC-》APB2ENR、GPIOX-》CRL、GPIOX-》ODR三个寄存器(GPIOX中的X表示为GPIO的组别,例如GPIOB表示在GPIO的B组上,因为一般单片机上的GPIO一般都会有很多组的,这样也只是为了区分这些IO罢了)

CRL 和 CRH 控制着每个 IO 口的模式及输出速率。


这里需要注意的是在配置GPIO前,都需要先使能该GPIO的时钟!(可能有人会问“为什么一定要是使能时钟呢?”答案是:GPIO也是外设的一种,然后外设是需要提供时钟信号工作,以便于设置GPIO的数据传输速度的高速/低速输出,所以有关数据的传输都是在时钟信号的基础上的。51单片的IO口也有时钟,只是为了方便,默认开启的,ST的为了更好的控制功耗,电路上做的可以选择开关时钟,降低功耗。补充网友的答案:“寄存器是基于触发器的,触发器的赋值是一定需要时钟的,而寄存器的时钟是由总线时钟提供的,就是说没有总线时钟的话,你给寄存器值它是不会读入的”)


然后设置两个LED的GPIO的模式,因为LED是输出设备,所以GPIO将设置成输出模式,其中配置模式要用到的寄存器为配置寄存器CRL和CRH。其中CRH为高位寄存器,CRL为低位寄存器。(有人可能有疑问为什么用的是32位的寄存器为什么还要为两个寄存器来管理呢?难道是32位还不够配置吗?答案是:是的,不够用。配置寄存器对一个GPIO的配置要用4个位来完成。一组GPIO有16个引脚,一个引脚要4位的话,那么16个引脚就要16*4=64,那么就要2个32位的寄存器来实现,所以就干脆把高八位给一个寄存器,低八位给另外一个寄存器,这就出现了CRL和CRH。也许有人还会有疑问说为什么一定要用4位来对一个引脚的配置呢?答案是肯定的,因为前面说了,对GPIO的配置首先配置为输入模式或者输出模式,然后再对输入、输出配置为哪种输出,哪种输入,是推挽输出呢,还是开漏输入呢,这都是需要至少2个位来进行配置,而且输入、输出模式各有4种模式,共8中,这样要以最少的资源来实现的话,用00、01、10、11这种2个位来实现是最省资源的,所以2*2=4,所以对于1个GPIO的模式的配置至少也就要4位了。)


接着就是查看LED的GPIO的引脚接在IC哪个引脚上。怎么查看LED的引脚到底接在哪呢?这个要看开发板的原理图,不同的板子来说或者对于不同的商家来说在GPIO的引脚接线上会有不同,以我的开发板的原理图可以发现LED灯的GPIO分别接在了GPIOB的pin5和GPIOE的pin5上,根据电路性质,一般会将LED设置为推挽输出模式(可能这时就会有人问了,为什么就是推挽呢?为什么不是开漏呢?我的理解是:这是有电路性质决定的,推挽顾名思义就是灌电流与拉电流都可以输出,通俗地讲就是输出高低电平。而开漏是一直输出低电平,当你要输出高电平的时候需要加上拉)

通过查阅寄存器的数据手册可知,00位通用推挽模式,那么

//======================================================//

GPIOB-》CRL |= 3 《《 20;//设置模数数据传输速率

GPIOB-》CRL &=~(3 《《 22);//设置GPIO模式

//======================================================//

这样就把GPIOB组的pin5设置成了通用推挽模式,前面说了一个引脚需要4个位来配置,查看寄存器数据手册可看到,前面2个位是设置通用推挽模式的,后面2个位是基于这种模式的数据传输速率,在这里我们悬着的是“最大速度50MHz”,而GPIOB-》CRL|=3《《20;就是设置数据传输速率为最大速度50MHz,GPIOB-》CRL&=~(3《《22);为设置GPIOB的pin5为通用推挽模式。


最后就是设置LED的初始状态了,再说之前先说一下ODR寄存器。


ODR是一个端口输出数据寄存器,也只用了低16位。该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口的输出电平。


所以在配置LED初始化的时候就需要调用这个寄存器设置LED的初始状态。

//======================================================//

GPIOE-》ODR|=1《《5;//设置LED的初始化状态为1(1为到电平,亮灭有其电路决定,我的开发板由于接了上拉,使得低电平才是有效的,即0为LED灯亮,1为LED灯灭)

//======================================================//

这样初始化函数就完成了。

具体初始化代码如下:

//=======================led.c===============================//

#include “stm32f10x.h”

#include “led.h”

void led_Init(void)

{

RCC -》APB2ENR |= 1 《《 3;//使能 PORTB 时钟

RCC -》APB2ENR |= 1 《《 6;//使能 PORTE 时钟

GPIOB -》CRL |= 3 《《 20;

GPIOB -》CRL &=~ (3 《《 22);//PB.5 推挽输出

GPIOB -》ODR |= 1 《《 5;//PB.5 输出高

GPIOE -》CRL |= 3 《《 20;

GPIOE -》CRL &= ~(3 《《 22);//PE.5 推挽输出

GPIOE -》ODR |= 1 《《 5;//PE.5 输出高

}

//======================================================//

其头文件如下:

//=========================led.h=============================//

#ifndef __LED_H_

#define __LED_H_

extern void led_Init(void);

#endif

//======================================================//

接下来说一下初始化好了,主函数要怎么写。

第一需要的是点亮LED的语句,在初始化中有提到,设置LED初始化状态即:GPIOB -》ODR |= 1 《《 5;//PB.5 输出高

那么同时也能使用这句来进行LED的亮灭控制。代码如下:

//==========================main.c============================//

#include “stm32f10x.h”

#include “led.h”

int main(void)

{

int i=0;

led_Init();//LED初始化

while(1)

{

GPIOB -》ODR &= ~ (1 《《 5);//PB.5输出低电平,即点亮LED

GPIOE -》ODR &= ~(1 《《 5);//PB.5输出低电平,即点亮LED

for(i=0;i《1000000;i++);//延时

GPIOB -》ODR |= 1 《《 5;//PE.5输出高电平,即灭掉LED

GPIOE -》ODR |= 1 《《 5;//PB.5输出高电平,即灭掉LED

for(i=0;i《1000000;i++);//延时

}

}

//======================================================//

整个跑马灯的工程就只需这三个文件,就能简单实现跑马的功能,如果你想要延时的时间是规律的,那么你就要去使用定时器了。因为定时器定时的时间是准时的,软件延时第一消耗CPU的资源,然后时间也不够精确,所以一般在对时间要求没有很精确的要求是才会使用软件延时的做法。

推荐阅读

史海拾趣

AVAGO公司的发展小趣事

随着公司规模的扩大和技术的不断积累,AVAGO公司开始寻求多元化的发展道路。除了继续深耕半导体领域外,公司还积极拓展其他相关领域,如传感器、无线通信等。同时,公司还与其他行业的领军企业建立了紧密的战略合作关系,共同推动技术创新和市场拓展。

Broyce Control公司的发展小趣事

进入21世纪,Broyce Control已经成为电气和电子产品设计、制造和供应领域的佼佼者。公司不仅在技术上持续创新,还注重与行业伙伴的合作与交流。通过参与国际标准的制定和推广,Broyce Control逐渐在行业内树立了领导地位。同时,公司还积极承担社会责任,推动可持续发展和环保理念的实践,为行业的健康发展做出了积极贡献。

DRS Technologies公司的发展小趣事

近年来,DRS在红外探测器技术领域取得了重要进展。公司在12μm像元尺寸非制冷红外探测器技术的基础上,成功研制出10μm像元尺寸的产品。这一技术的突破不仅展示了DRS在红外探测领域的实力,也为其在未来的市场竞争中提供了有力的技术支撑。

BREL International Components公司的发展小趣事

面对日益激烈的国际竞争,BREL International Components公司开始实施全球化战略。公司首先在亚洲设立生产基地,利用当地的低成本优势提高生产效率。随后,BREL在欧洲和北美设立研发中心,吸纳全球顶尖的技术人才,推动技术创新和产品升级。通过全球化的战略布局,BREL不仅降低了生产成本,还拓宽了市场渠道,实现了业务的快速增长。

DSMI Electronics SA公司的发展小趣事

DSMI Electronics SA公司始于20世纪90年代末,由一群志同道合的电子工程师和科研人员创立。在创业初期,公司就明确了自己的技术定位:专注于电子元件和模块的研发与生产,特别是针对嵌入式系统和工业自动化领域。他们凭借深厚的技术积累和不懈的努力,成功开发出了一系列性能稳定、质量可靠的电子元件,赢得了市场的初步认可。

Electromagnetic Industries Llp公司的发展小趣事

EMI公司成立于20世纪80年代初,当时正值电子行业的快速发展期。创始人李先生凭借对电磁技术的深厚理解和市场洞察,决定投身电磁产品的研发与生产。初创时期,公司面临着资金短缺、技术难题和市场认可度低等多重挑战。然而,李先生和他的团队并没有放弃,他们通过夜以继日的研发,不断攻克技术难关,同时积极寻找合作伙伴,拓展销售渠道。经过几年的努力,EMI公司逐渐在市场上站稳了脚跟。

问答坊 | AI 解惑

硬件设计鸡毛蒜皮之一

鸡毛蒜皮之一:成本节约 现象一:这些拉高/拉低的电阻用多大的阻值关系不大,就选个整数5K吧 点评:市场上不存在5K的阻值,最接近的是4.99K(精度1%),其次是5.1K(精度5%),其成本分别比精度为20%的4.7K高4倍和2倍。20%精度的电阻阻值只 ...…

查看全部问答>

RC有源带通滤波器的设计

RC有源带通滤波器的设计 …

查看全部问答>

3W LED驱动整流桥输入端开槽与否

对之前3*1W的线路板进行改版;整流桥输入端是否需要开槽(多宽?);一个专业工程师说不用开;桥堆本身就过了安规的;现在市面上我买的大多数产品,输入端也大多有开槽的; 不知道有经验人士怎么看待这个问题?…

查看全部问答>

如何将uboot linux内核 应用程序做成bin文件烧录

我自己做的一个嵌入式应用程序 现在我需要将uboot linux内核 然后和自己的应用程序 打包做成一个bin文件烧写入板子 请问应该怎么做啊? 本人对烧录知道的不多 请大家帮忙   说得越仔细越好   3Q…

查看全部问答>

WinCE驱动程序的存在形式及其意义

    所有基于WinCE的驱动程序都是以用户态下的dll文件的形式存在的。也就是说,一方面所有WinCE下的驱动程序都是运行在用户态下的,另一方面我们开发WinCE驱动程序的方法与开发普通的win32dll是完全一致的,还需要补充的一点就是所有的 ...…

查看全部问答>

能否不用IC自己做个简单的12V转5V的DC-DC转换电路?

想尽可能的降低成本,所以请教能否不用IC自己做个简单的12V转5V的DC-DC转换电路?…

查看全部问答>

分享我CRC校验的源代码给大家

-- ****************************************************************************--版权所有:www.eeleader.com--------------------------------------------------------------设计日期:2010.3.4------------------------------------------- ...…

查看全部问答>

D-LC谐振放大器D题172697592群

本帖最后由 paulhyde 于 2014-9-15 08:58 编辑 D-LC谐振放大器D题172697592群D-LC谐振放大器D题172697592群  …

查看全部问答>

STM32F107如何强制工作于设备模式

我在硬件设计的时候没有设计ID线,只需要STM32F107工作于设备模式即可,请问下如何让STM32F107强制工作在设备模式下呢,就是检测到VBUS以后能够上拉DP…

查看全部问答>

DCO_Library.h!如何正确使用?DCO产生标准的时钟信号!

我想利用DCO_Library.h 和DCO_Library.s43,通过DCO产生标准的时钟信号 根据DC0_ Library.h可以产生的标准的1MHz 或2MHz 但我使用时 有 Error[e46]: Undefined external \"TI_SetDCO\" referred in main ( E:\\桌面20120730\\Msp430\\铁电\\MSP430 ...…

查看全部问答>