【1】STM32F103嵌入式编程之路:跑马灯与呼吸灯

跑马灯

跑马灯是STM32编程中最经典的第一个项目,目的是让操作者去熟悉GPIO的用法,包括一些最基本的操作和概念。

文中涉及到一些基本的C语言知识和库函数概念等,我会尽可能一一详解。

打好基础是学习一门庞杂课程的重中之重,所以前期我会事无巨细地详加解释。

硬件部分

首先我们看电路图
电路图
不难发现,当左侧(PF端)为低电平时,发光二极管就会被点亮。得到对应关系如下:
对应关系

软件部分

这里我们采用我所使用的“神舟三号”给出的实例代码作为学习的材料,没有的同学可以去搜索并下载,获取起来很容易。

这里官方的写法是使用库函数直接进行操作,这里我需要强调一点,如果只熟悉库函数而不了解其寄存器对应的写法,是无法在工作中真正学会灵活应用的。所以在我们熟悉库函数的用法以后,我们会使用寄存器再来进行一次改写。

头文件及定义部分

#include "stm32f10x.h"

stm32f10x.h 这个头文件是STM32开发最为重要的一个头文件,涉及到寄存器地址到寄存器结构体变量的映射。

#define RCC_GPIO_LED                    RCC_APB2Periph_GPIOF    /*RCC是ARM里面复位与时钟控制器(Reset Clock Controller)缩写,这里使用的是给GPIOF组所配置的RCC*/
#define LEDn                            4                       /*定义LED的数量*/
#define GPIO_LED                        GPIOF                   /*LED灯使用的GPIO组,从A到G一共有8组,这里使用的是F组*/

#define DS1_PIN                         GPIO_Pin_6              /**/
#define DS2_PIN                         GPIO_Pin_7              /**/
#define DS3_PIN                         GPIO_Pin_8              /**/
#define DS4_PIN                         GPIO_Pin_9              /**/

这里先定义好了LED灯相关的一系列参数。define的用法如下:

#define  标识符  常量   //注意, 最后没有分号

#define又称宏定义,标识符为所定义的宏名,简称宏。标识符的命名规则与前面讲的变量的命名规则是一样的。#define 的功能是将标识符定义为其后的常量。一经定义,程序中就可以直接用标识符来表示这个常量。

GPIO_InitTypeDef GPIO_InitStructure;//创造类型为GPIO_InitTypeDef的结构体

ErrorStatus HSEStartUpStatus;//创造类型为ErrorStatus的枚举结构体

u8 count=0;//创造count以用来计数

跟踪GPIO_InitTypeDef 的代码:

typedef struct
{
  uint16_t GPIO_Pin;             /*GPIO的针脚号 */

  GPIOSpeed_TypeDef GPIO_Speed;  /*输出口的频率*/

  GPIOMode_TypeDef GPIO_Mode;    /*输出的模式*/
}GPIO_InitTypeDef;

跟踪 ErrorStatus的代码:

typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;   /*用于判断是否发生错误*/

除了完成这些基本组件的搭建,我们还要继续进行后续要用到的函数的声明,如下:

void Delay(vu32 nCount);/*控制灯与灯之间的时间间隔*/
void Turn_On_LED(u8 LED_NUM);/*打开LED灯触发的函数*/

功能函数模块

按输入序号打开LED灯

void Turn_On_LED(u8 LED_NUM)
{
	switch(LED_NUM)
	{
        case 0:
          GPIO_ResetBits(GPIO_LED,DS1_PIN);  /*打开DS1*/
          break;
        case 1:
          GPIO_ResetBits(GPIO_LED,DS2_PIN);  /*打开DS2*/
          break;
        case 2:
          GPIO_ResetBits(GPIO_LED,DS3_PIN);  /*打开DS3*/
          break;
        case 3:
          GPIO_ResetBits(GPIO_LED,DS4_PIN);  /*打开DS4*/
          break;          
        default:
		  GPIO_ResetBits(GPIO_LED,DS1_PIN|DS2_PIN|DS3_PIN|DS4_PIN); /*都不是的情况下打开四个灯,正常情况下不可能发生,也可视作为报错*/
          break;
	}
}

控制灯与灯之间的时间间隔

void Delay(vu32 nCount)
{
  for(; nCount != 0; nCount--);//设定一个延迟,递减为0以后退出函数
}

主函数

int main(void)
{
  	RCC_APB2PeriphClockCmd(RCC_GPIO_LED, ENABLE); /*开启时钟*/
  	GPIO_InitStructure.GPIO_Pin = DS1_PIN|DS2_PIN|DS3_PIN|DS4_PIN;  /*设定结构体内的四个针脚*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*输出模式设定为推挽模式*/
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*输出频率定位50MHz*/
  
  	GPIO_Init(GPIO_LED, &GPIO_InitStructure);  /*将GPIO组与创建的GPIO结构体进行绑定初始化*/
  	GPIO_SetBits(GPIO_LED,DS1_PIN|DS2_PIN|DS3_PIN|DS4_PIN);	/*将4个LED灯设置为 高电平,即设置为熄灭状态*/

  	while(1)
  	{	
	GPIO_SetBits(GPIO_LED,DS1_PIN|DS2_PIN|DS3_PIN|DS4_PIN);	/*先使得4个灯全部熄灭*/
		Turn_On_LED(count%4);	/*使计数器模除4,可以保证结果在0~3之间*/		
		count++;		/*使得计数器不断累增,让计数器不断在0~3之内循环*/	
		Delay(0x2FFFFF);		/*启动延时函数,让 灯与灯之间有间隔*/
  	}
  
}

改造为呼吸灯程序

呼吸灯原理分析

PulseWidthModulation脉冲宽度调制,简称PWM。

PWM(脉冲宽度调制)对模拟信号电平进行数字编码的方法,计算机只能输出0或5V的数字电压值而不能输出模拟电压,而我们如果想获得一个模拟电压值,则需通过使用高分辨率计数器,改变方波的占空比来对一个模拟信号的电平进行编码

设备仍输出数字信号,因为满幅值的直流供电只有高电平(1)和低电平(0)两种。电压是以一种连接(1)或断开(0)的重复脉冲序列被夹到模拟负载上去的,连接即是直流供电输出,断开即是直流供电断开。通过对连接和断开时间的控制,只要带宽足够,可以输出任意不大于最大电压值的模拟电压。

所以我们只要控制高低电平的占空比,就可以用PWM模拟出呼吸灯的效果。

在这里插入图片描述

主函数

int main(void)
{
	int i,b=4000;/*定义两个参数作为呼吸灯的高低电平比例控制,b的值可以自己设定*/
  	RCC_APB2PeriphClockCmd(RCC_GPIO_LED, ENABLE); /*开启时钟*/

  	GPIO_InitStructure.GPIO_Pin = DS1_PIN|DS2_PIN|DS3_PIN|DS4_PIN; 
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  
  	GPIO_Init(GPIO_LED, &GPIO_InitStructure);  /*绑定GPIO组和结构体并初始化*/
  	GPIO_SetBits(GPIO_LED,DS1_PIN|DS2_PIN|DS3_PIN|DS4_PIN);/*使所有灯先熄灭*/
		

  	while(1)/*开启无限循环*/
  	{	
		for(i=0;i<b;i++){						/*正向模拟PWM*/
			GPIO_ResetBits(GPIO_LED,DS1_PIN);	/*将灯打开*/
			Delay(b-i);							/*控制本次灯亮的时间*/
			GPIO_SetBits(GPIO_LED,DS1_PIN);		/*将灯熄灭*/
			Delay(i);							/*控制本次灯灭的时间*/
		}
		for(i=0;i<b;i++){						/*反向模拟PWM*/
			GPIO_ResetBits(GPIO_LED,DS1_PIN);	/*将灯打开*/
			Delay(i);							/*控制本次灯亮的时间*/
			GPIO_SetBits(GPIO_LED,DS1_PIN);		/*将灯熄灭*/
			Delay(b-i);							/*控制本次灯灭的时间*/
		}
  	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44234948/article/details/85230175