STM32系列文章01:流水灯

接触单片机的第一个实验就是跑马灯实验,虽说这个实验比较简单,但万事开头难。闲话少说,直接上代码:

跑马灯实验的实现大致可以分为三个步骤:

1. LED初始化

LED初始化包括时钟初始化和引脚初始化。每一个外设都有其对应的时钟,所以在使用外设之前都要先开启外设的时钟。

引脚初始化(端口初始化),本实验是要通过引脚输出高低电平来控制LED的亮灭。端口初始化主要是配置:输出引脚、输出模式、输出速率。

时钟及端口初始化代码如下:


/*本次跑马灯实验主要用到PB6~PB9几个端口*/
#include "LED.h"

void LED_Init(void)  //自定义Led灯初始化函数
{  
    GPIO_InitTypeDef  GPIO_InitStructure;  
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA, ENABLE); /*使能LED灯使用的GPIO时钟*/  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);
        
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //以上为配置GPIO
    
    GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB 
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin =     
    GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //以上为配置GPIO
    
    GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //以上为配置GPIO
    
    GPIO_Init(GPIOD, &GPIO_InitStructure); //初始化GPIOD  
}  

附上头文件:


/*头文件中主要是一些宏定义和位带操作*/
#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"

#define LED1_ON GPIO_SetBits(GPIOB, GPIO_Pin_6)
#define LED1_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_6)
#define LED2_ON GPIO_SetBits(GPIOB, GPIO_Pin_7)
#define LED2_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_7)
#define LED3_ON GPIO_SetBits(GPIOB, GPIO_Pin_8)
#define LED3_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8)
#define LED4_ON GPIO_SetBits(GPIOB, GPIO_Pin_9)
#define LED4_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_9)
#define LED5_ON GPIO_SetBits(GPIOA, GPIO_Pin_0)
#define LED5_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_0)
#define LED6_ON GPIO_SetBits(GPIOA, GPIO_Pin_1)
#define LED6_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_1)
#define LED7_ON GPIO_SetBits(GPIOA, GPIO_Pin_2)
#define LED7_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_2)
#define LED8_ON GPIO_SetBits(GPIOA, GPIO_Pin_3)
#define LED8_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_3)
#define LED9_ON GPIO_SetBits(GPIOC, GPIO_Pin_3)
#define LED9_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_3)
#define LED10_ON GPIO_SetBits(GPIOC, GPIO_Pin_2)
#define LED10_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_2)
#define LED11_ON GPIO_SetBits(GPIOC, GPIO_Pin_1)
#define LED11_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_1)
#define LED12_ON GPIO_SetBits(GPIOC, GPIO_Pin_0)
#define LED12_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_0)
#define LED13_ON GPIO_SetBits(GPIOC, GPIO_Pin_10)
#define LED13_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_10)
#define LED14_ON GPIO_SetBits(GPIOC, GPIO_Pin_11)
#define LED14_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_11)
#define LED15_ON GPIO_SetBits(GPIOC, GPIO_Pin_12)
#define LED15_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_12)
#define LED16_ON GPIO_SetBits(GPIOC, GPIO_Pin_13)
#define LED16_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_13)

	 
//位带操作,实现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)  //输入

#define LED1  PBout(6)
#define LED2  PBout(7)
#define LED3  PBout(8)
#define LED4  PBout(9)
#define LED5  PAout(0)
#define LED6  PAout(1)
#define LED7  PAout(2)
#define LED8  PAout(3)
#define LED9  PCout(3)
#define LED10  PCout(2)
#define LED11  PCout(1)
#define LED12  PCout(0)
#define LED13  PCout(10)
#define LED14  PCout(11)
#define LED15  PCout(12)
#define LED16  PCout(13)

void LED_Init(void);

#endif

2.延时函数的编写

跑马灯实验中需要用到延时函数,这个网上有很多例子。下面给出一个比较常用且精准的延时函数。感兴趣的朋友也可以自己去编写属于自己的延时函数。这个函数比较常用,在后面的实验中也会经常用到。

源文件:


#include "delay.h"

void delay_us(u32 nus)
{
	u32 temp;
	SysTick->LOAD = 9*nus;
	SysTick->VAL=0X00;//清空计数器
	SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
	do
	{
		temp=SysTick->CTRL;//读取当前倒计数值
	}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
    SysTick->CTRL=0x00; //关闭计数器
    SysTick->VAL =0X00; //清空计数器
}

void delay_ms(u16 nms)
{
	u32 temp;
	SysTick->LOAD = 9000*nms;
	SysTick->VAL=0X00;//清空计数器
	SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
	do
	{
		temp=SysTick->CTRL;//读取当前倒计数值
	}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
    SysTick->CTRL=0x00; //关闭计数器
    SysTick->VAL =0X00; //清空计数器
}

头文件:


#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f10x.h"

void delay_us(u32 nus);
void delay_ms(u16 nms);

#endif

主函数就相对比较简单了,调用LED的初始化函数,依次点亮LED。本实验中使用的是位带操作,也可以用上面提供的第一种方式操作,当然网上也有其他的如直接操作寄存器的,都是可以的,因人而异。


/*引用头文件是为了调用其中定义的函数*/
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"

int main(void)
{   
    LED_Init(); 
    while(1)
    {     
        LED1 = 1;
        delay_ms(200);
        LED1 = 0;
        delay_ms(200);
        LED2 = 1;
        delay_ms(200);
        LED2 = 0;
        delay_ms(200);
        LED3 = 1;
        delay_ms(200);
        LED3 = 0;
        delay_ms(200);
        LED4 = 1;
        delay_ms(200);
        LED4 = 0;
        delay_ms(200);
    }   
}

至此,跑马灯实验就结束了。本实验仅供有需要的朋友参考,切记不可照抄照搬,希望在看的时候加入一些自己的思考,根据自己的原理图来做相应的修改。


参考资料:

STM32中文参考手册_V10

Cortex-M3权威指南(中文)

资料下载地址:https://pan.baidu.com/s/16W3K2zkJJBOaxYb4jJctgg 

提取码:wwkn 

完整工程下载链接:https://pan.baidu.com/s/109sOF0eroGE0bHeTTM1Fww

写在最后:

如果上面的下载链接失效了,可以私信我给你们提供资料。




如果你喜欢我的文章,可以随意打赏我,以激励我继续写下去。谢谢!