单片机入门学习四 STM32单片机学习一 跑马灯程序和创建工程

STM32学习了有一阵子了,现做一个简单的总结。
先上一个跑马灯的小程序,本人学习过程中先学习了一些STM32F103的一些基本知识,但是直到接触到真实的程序后开始思索看代码后一些模糊的概念才发现原先学习的基础感念都提到过,不过当时在学习的时候仅仅过了遍脑子没有理解了,故在此我先贴出跑马灯的程序,然后通过程序拓展出STM32编程必知的基础。

1、跑马灯

什么是跑马灯,本篇的程序实现的功能是板子上的两个小灯来回的闪烁。
下面将贴出寄存器版和库函数版两版源码。

1)寄存器版

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
 {  
    delay_init();   //延时函数初始化     
    LED_Init();     //初始化与LED连接的硬件接口
    while(1)
    {
        LED0=0;
        LED1=1;
        delay_ms(300);  //延时300ms
        LED0=1;
        LED1=0;
        delay_ms(300);  //延时300ms
    }
 }
 /**下面注释掉的代码是通过 直接操作寄存器 方式实现IO口控制**
int main(void)
{ 
    delay_init();   //初始化延时函数
    LED_Init();     //初始化LED端口
    while(1)
    {
     GPIOB->BRR=GPIO_Pin_5;//LED0亮
     GPIOE->BSRR=GPIO_Pin_5;//LED1灭
     delay_ms(300);
     GPIOB->BSRR=GPIO_Pin_5;//LED0灭
     GPIOE->BRR=GPIO_Pin_5;//LED1亮
     delay_ms(300);

     }
 }
***************************************************/

sys.h

#ifndef __SYS_H
#define __SYS_H 
#include "stm32f10x.h"

//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS       0       //定义系统文件夹是否支持UCOS

//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#endif

led.h

#ifndef __LED_H
#define __LED_H  
#include "sys.h"

#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5 

void LED_Init(void);//初始化                           
#endif

led.c

#include "led.h"

//初始化PB5和PE5为输出口.并使能这两个口的时钟         
//LED IO初始化
void LED_Init(void)
{
    RCC->APB2ENR|=1<<3;    //使能PORTB时钟       
    RCC->APB2ENR|=1<<6;    //使能PORTE时钟  

    GPIOB->CRL&=0XFF0FFFFF; 
    GPIOB->CRL|=0X00300000;//PB.5 推挽输出       
    GPIOB->ODR|=1<<5;      //PB.5 输出高

    GPIOE->CRL&=0XFF0FFFFF;
    GPIOE->CRL|=0X00300000;//PE.5推挽输出
    GPIOE->ODR|=1<<5;      //PE.5输出高 
}

delay.h

#ifndef __DELAY_H
#define __DELAY_H              
#include "sys.h"  
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif

delay.c

#include "delay.h"
static u8  fac_us=0;//us延时倍乘数              
static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);   //选择外部时钟  HCLK/8
    fac_us=SystemCoreClock/8000000;             //为系统时钟的1/8  
    fac_ms=(u16)fac_us*1000;                    
}                                   
//延时nus
//nus为要延时的us数.                                             
void delay_us(u32 nus)
{       
    u32 temp;            
    SysTick->LOAD=nus*fac_us;//时间加载          
    SysTick->VAL=0x00;//清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;//开始倒数    
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
    SysTick->VAL =0X00;//清空计数器   
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{                 
    u32 temp;          
    SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;//清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;//开始倒数  
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
    SysTick->VAL =0X00;//清空计数器          
} 
#endif 
2、库函数版

main.c

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"

int main(void)
{ 

    delay_init();   //初始化延时函数
    LED_Init();     //初始化LED端口
    while(1)
    {
            GPIO_ResetBits(GPIOB,GPIO_Pin_5);  //LED0对应引脚GPIOB.5拉低,亮  等同LED0=0;
            GPIO_SetBits(GPIOE,GPIO_Pin_5);   //LED1对应引脚GPIOE.5拉高,灭 等同LED1=1;
            delay_ms(300);  //延时300ms
            GPIO_SetBits(GPIOB,GPIO_Pin_5);    //LED0对应引脚GPIOB.5拉高,灭  等同LED0=1;
            GPIO_ResetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉低,亮 等同LED1=0;
            delay_ms(300);                     //延时300ms
    }
} 

led.h

#ifndef __LED_H
#define __LED_H  
void LED_Init(void);//初始化                           
#endif

led.c

#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟         
//LED IO初始化
void LED_Init(void)
{
 GPIO_InitTypeDef  GPIO_InitStructure;

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);//使能PB,PE端口时钟

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);//PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//LED1-->PE.5 端口配置, 推挽输出
 GPIO_Init(GPIOE, &GPIO_InitStructure);//推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5);//PE.5 输出高 
}

上面的代码即是一个跑马灯程序的源码了,其中sys.h为正点原子为我们可以实现51类似的GPIO控制功能,定义的一些宏定义。

2、罗列问题

看了上述的单片机入门学习代码(类似语言学习入门代码helloword),想必会有很多疑问提出来,下面会罗列一些学习方面的问题,然后在一个一个的给予解释。

1)开发工具使用MDK5,工程项目如何建立
2)GPIO是什么,程序中用到的GPIOB、GPIOE这些宏定义代表什么
3)stm32f103是什么
4)为何要使能PB、PE端口时钟
5)库函数编程函数都表示的是什么

3、解释问题

1)开发工具使用MDK5,工程项目如何建立

1-1)使用MDK5新建工程,工程建好后,通过右键工程项目菜单“Manage project items…”将相应的基本开发库函数源码填入进来,下图是本程序用到的基础库的介绍,请看图
工程目录说明
要使程序编译成功,还需在 “Options for target ‘marquee’”弹出框中设置 “C/C++”相应配置项
①设置“Preprocessor Symbols”,如下图
Preprocessor Symbols
②设置 “Include Paths”,即设置相应库函数的目录,如下图
Include Paths

USE_STDPERIPH_DRIVER :表示是否在应用中启用外设驱动。我们使用标准外设库是为了方便控制外设,添加这个定义,以启用外设驱动。( to use or not the peripheral’s drivers in application code (i.e. code will be based on direct access to peripheral’s registers rather than drivers API) , this option is controlled by the #define USE_STDPERIPH_DRIVER

STM32F10X_HD:STM32F10X系列每种系列都有所区别,例如sram或者flash或者外设数量不一样,所以stm32标准外设库必须根据你使用的处理器来做相应的预处理。
在stm32f10x.h可以看到相应的宏定义,如下:

#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL) 
  /* #define STM32F10X_LD */     /*!< STM32F10X_LD: STM32 Low density devices */
  /* #define STM32F10X_LD_VL */  /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */  
  /* #define STM32F10X_MD */     /*!< STM32F10X_MD: STM32 Medium density devices */
  /* #define STM32F10X_MD_VL */  /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */  
  /* #define STM32F10X_HD */     /*!< STM32F10X_HD: STM32 High density devices */
  /* #define STM32F10X_HD_VL */  /*!< STM32F10X_HD_VL: STM32 High density value line devices */  
  /* #define STM32F10X_XL */     /*!< STM32F10X_XL: STM32 XL-density devices */
  /* #define STM32F10X_CL */     /*!< STM32F10X_CL: STM32 Connectivity line devices */
#endif
#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)
 #error "Please select first the target STM32F10x device used in your application (in stm32f10x.h file)"
#endif

1-2)采用ST-Link下载程序
使用ST-Link方式下载程序我们可以使用debug功能在线调试代码,建议开发阶段使用这种方式。
ST-Link方式下载程序设置方式如下图:
st-link
在debug页签中选中“ST-Link debugger”,点击“Setting”修改 Debug Adpater为“ST-Link/V2”,
安照图设置”CPU DLL”为SARMCM3.DLL、“Parameter”为 -REMAP;设置“Debug DLL”为DARMSTM.DLL、“Parameter”为-pSTM32F103ZE;设置“Driver DLL”为SARMCM3.DLL;设置“Dialog DLL”为TARMSTM.DLL、Parameter”为-pSTM32F103ZE;
除了上图设置还需要设置“Cortex-M Target Driver Setup”中的“Flash Download”选中 Reset and Run,如下图
run
ST-Link实物连接如下图:
实物连接
1-3)修改程序编译结果为hex文件
mdk5默认输出是不选中Create Hex File选项的,我们需要选中,如下图:
outfile

按照上面步骤设置好环境后,编译下载到板子上,跑马灯程序即可运行,运行效果如下图:
实物效果

碍于篇幅长度,问题中的2~5后续文章会一一解释记录。

  • 60
    点赞
  • 270
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
$PBExportComments$用数据窗口做跑马 release 7; datawindow(units=1 timer_interval=10 color=15780518 processing=0 HTMLDW=no print.documentname="" print.orientation = 0 print.margin.left = 24 print.margin.right = 24 print.margin.top = 24 print.margin.bottom = 24 print.paper.source = 0 print.paper.size = 0 print.prompt=no print.buttons=no print.preview.buttons=no ) summary(height=0 color="536870912" ) footer(height=0 color="536870912" ) detail(height=87 color="536870912" ) table(column=(type=char(10) updatewhereclause=yes name=s dbname="s" ) ) text(band=header alignment="1" text="S:" border="0" color="0" x="13" y="6" height="14" width="18" name=s_t font.face="宋体" font.height="-9" font.weight="400" font.family="0" font.pitch="2" font.charset="134" background.mode="1" background.color="536870912" ) column(band=detail id=1 alignment="0" tabsequence=10 border="0" color="0" x="2" y="32" height="1" width="16" format="[general]" name=s edit.limit=0 edit.case=any edit.focusrectangle=no edit.autoselect=yes edit.autohscroll=yes font.face="宋体" font.height="-9" font.weight="400" font.family="0" font.pitch="2" font.charset="134" background.mode="1" background.color="536870912" ) text(band=detail alignment="0" text="现在跑马可以用了!" border="0" color="8421376" x="0~t333 - (long(left(right(string(t),4),3))) /1.5" y="0" height="33" width="279" name=t_1 font.face="Arial" font.height="-20" font.weight="700" font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="553648127" ) compute(band=summary alignment="0" expression="left(string(cpu()) + string(today(),'yyyymmdd'),len(string(cpu()) + string(today(),'yyyymmdd')) -8)"border="0" color="0" x="43" y="11" height="19" width="371" format="[general]" name=t visible="1~t0" font.face="Arial" font.height="-12" font.weight="400" font.family="2" font.pitch="2" font.charset="0" background.mode="2" background.color="16777215" ) text(band=detail alignment="0" text="/*truncate((long(left(right(string(t),4),3)) /50),0) * 50* (rgb(255,255,255) /1000)*/rand(100)/100 * rgb(255,255,255)" border="0" color="16711935" x="1" y="44" height="14" width="1418" name=t_2 font.face="Arial" font.height="-8" font.weight="700" font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="553648127" ) text(band=detail alignment="0" text="333 - (long(left(right(string(t),4),3))) /1.5" border="0" color="16711935" x="0" y="66" height="14" width="508" name=t_3 font.face="Arial" font.height="-8" font.weight="700" font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="553648127" ) htmltable(border="1" ) htmlgen(clientevents="1" clientvalidation="1" clientcomputedfields="1" clientformatting="0" clientscriptable="0" generatejavascript="1" )
要实现stm32单片机跑马功能,可以使用GPIO引脚控制LED的亮灭。首先需要将引脚配置为输出模式,使用GPIO_Init函数来实现。 在stm32中,可以使用库函数来控制GPIO引脚的状态。其中,GPIO_InitStruct是GPIO_Init函数的参数,用于配置GPIO引脚的参数。 接下来,我们需要编写相关的代码,实现4种跑马模式。这里我们采用循环控制LED的亮灭,并且通过修改GPIO引脚状态来实现。 第一种跑马模式是顺时针依次点亮4个LED,可以使用for循环来实现。依次将每个引脚配置为高电平,延时一段时间后再配置为低电平,循环执行。 第二种跑马模式是逆时针依次点亮4个LED,同样可以使用for循环实现。依次将每个引脚配置为高电平,延时后再配置为低电平,循环执行。 第三种跑马模式是两边往中间点亮,可以使用两个for循环来实现。先将左边和右边的引脚点亮,延时后再将中间的引脚点亮,再延时后循环执行。 第四种跑马模式是中间往两边点亮,同样可以使用两个for循环实现。先将中间的引脚点亮,延时后再将左边和右边的引脚点亮,再延时后循环执行。 以上就是实现stm32单片机4种跑马亮灭的方法。通过控制GPIO引脚的状态,可以实现LED的点亮和熄灭,从而实现各种跑马效果。编写相应的代码,配置相应的GPIO引脚,通过循环控制LED的亮灭,即可实现跑马功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风水月

从心底相信自己是成功的第一步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值