学习的教程:杨桃电子《STM32入门100步》
使用的开发板:STM32_Mini
目录
板载LED原理图及LED点亮的条件
在开始之前,万年不变的就是配置LED需要使用的GPIO。在查看原理图得知,此开发板的LED0连接的引脚为PA8,需要置低电平(0),LED0才会点亮。
点亮LED需要注意的事:
外围电路的不同,LED点亮的条件也不同,灯亮是因为两侧有电压差。此开发板的LED0是阴极(-)与PA8连接,阳极(+)与VCC3.3连接,那么,当PA8输出高电平时,LED两端无电压差,电流不流动,所以LED0不会点亮。反之,PA8输出低电平时,LED0被点亮。
同理可得,若LED0阴极接GND,正极接PA8,当PA8置高电平时,LED0也会亮,置于低电平时,LED0不会亮。
有用到的几种设置GPIO状态的函数:
- GPIO_SetBits,设置指定的数据端口位
- GPIO_ResetBits, 清除指定的数据端口位
- GPIO_WriteBit, 设置或者清除指定的数据端口位
因此,led的初始化代码如下:
/*此为led.c的代码*/
#include "led.h"
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitTStruct;
/*第一步,打开外设的时钟*/
RCC_APB2PeriphClockCmd(LED0_GPIO_CLK, ENABLE);//A8
/*第二步,配置外设初始化结构体*/
GPIO_InitTStruct.GPIO_Pin = LED0_GPIO_PIN;
GPIO_InitTStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitTStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED0_GPIO_PORT,&GPIO_InitTStruct);
/*第三步,调用外设初始化函数,把配置好的结构体成员写到寄存器里面*/
GPIO_SetBits(LED0_GPIO_PORT,LED0_GPIO_PIN);//置高电平,LED初始状态为灭
}
/*此为led.h的代码*/
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define LED0_GPIO_CLK RCC_APB2Periph_GPIOA /* GPIO端口时钟 */
#define LED0_GPIO_PORT GPIOA /* GPIO端口 */
#define LED0_GPIO_PIN GPIO_Pin_8
#define LED0_OFF GPIO_WriteBit(LED0_GPIO_PORT,LED0_GPIO_PIN,(BitAction)(1))
#define LED0_ON GPIO_WriteBit(LED0_GPIO_PORT,LED0_GPIO_PIN,(BitAction)(0))
void LED_GPIO_Config(void);
#endif /*__LED_H*/
几种LED使用实例
第一种:LED常亮
单独的点亮LED0
#include "stm32f10x.h"
#include "led.h"
int main(void)
{
LED_GPIO_Config();
while(1)
{
LED0_ON;/*PA8输出低电平,led点亮*/
}
}
第二种:LED闪烁
延时函数
在编写程序前,需要有一个延时函数。我是用的江协科技的Delay函数模块。
1-3 Delay函数模块 https://www.alipan.com/s/78wxYsgg8uG 点击链接保存,或者复制本段内容,打开「阿里云盘」APP
在前一种LED常亮的主函数中进行更改,添加了延时函数。
led灯点亮,延时一秒,熄灭,延时一秒,再点亮
#include "stm32f10x.h"
#include "led.h"
#include "Delay.h"
int main(void)
{
LED_GPIO_Config();
while(1)
{
LED0_ON;/*PA8输出低电平,led点亮*/
Delay_s(1);/*延时1s*/
LED0_OFF;
Delay_s(1);
}
}
第三种:呼吸灯
视觉暂留,亮度占空比
(详见《STM32入门100步》P117)
在点亮部分的延时值为5us,熄灭500us时,在一个亮和灭的周期内,亮短熄灭长。由于视觉暂留现象,肉眼看到的LED亮度变更低。因此,通过延时值改变亮灭时间的占比,来改变LED的亮度。
LED 呼吸灯程序最终呈现的效果,就是刚刚实验所看到的呼吸灯效果。呼吸灯效果从过程上可拆分成两个部分:一是亮度逐渐变亮,二是亮度逐渐变暗。
菜单切换
把不同功能的程序放入不同的菜单值判断中,通过改变菜单值来切换运行不同的程序。
在主函数的开头,定义了三个变量
uint8_t MENU;//菜单值
uint16_t t,i;
/*变量t 是延时参数,t值增加,灯亮时间变长,则(501-t)是灯熄灭的时间*/
点亮和熄灭的一个周期总时长还是 501μ s,通过变量 t 的不断变化,就改变了 501μ s 中点亮和熄灭 LED 的时间,实现了占空比控制。
#include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h>
#include "Delay.h"
#include "led.h"
#include <stdint.h>
int main(void)
{
uint8_t MENU;
uint16_t t,i;
LED_GPIO_Config();
MENU = 0;
t = 1;
while(1)
{
if(MENU == 0) /*菜单0*/
{
for(i = 0;i < 10;i ++) /*目的:每循环10次,t的值才会加1*/
{
LED1_ON;
Delay_us(t);
LED1_OFF;
Delay_us(501 - t);
}
t ++;
if(t == 500)
{MENU = 1;}
}
if(MENU == 1) /*菜单1*/
{
for(i = 0;i < 10;i ++)
{
LED1_ON;
Delay_us(t);
LED1_OFF;
Delay_us(501 - t);
}
t --;
if(t == 1)
{
MENU = 0;
}
}
}
}
第四种:按键控制LED
通过原理图可知,KEY0连接的是PC1。在按键按下的时候,PC1接地,这个时候,I/O端口短接到电源负极。可以使用“GPIO_ReadInputDataBit”函数来读取I/O端口的电平状态。
所以,按下KEY0,低电平,松开KEY0,高电平。
配置KEY0
tips:当按键没有按下时需要让 I/O 端口保持在高电平。所以这里使用上拉电阻模式,即在按键没有被按下时让按键所连接的 I/O 端口没有连接 GND,是未接任何电路的悬空状态。设置为内部上拉电阻,I/O 端口则在悬空时仍保持高电平状态。当按键被按下时,I/O 端口与GND 短接,被 GND 强制变成低电平,单片机只要通过读取按键是高电平还是低电平,就能知道按键是否被按下。因为设置了 I/O 端口为上拉电阻输入模式。(详见《STM32入门100步》P121)
/*key.c*/
void KEY_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/*开外设相关的时钟*/
RCC_APB2PeriphClockCmd(KEY0_GPIO_CLK, ENABLE);
//配置外设相关的结构体
GPIO_InitStruct.GPIO_Pin = KEY0_GPIO_PIN |KEY1_GPIO_PIN;/*KEY1还没用到*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //输入,上拉电阻
GPIO_Init(KEY0_GPIO_PORT,&GPIO_InitStruct);
/*
GPIO_Speed只针对IO端口在输出模式才有效
*/
}
/*key.h*/
#include "stm32f10x.h"
/*KEY0 PC1 KEY1 PC13*/
#define KEY0_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY0_GPIO_PORT GPIOC
#define KEY0_GPIO_PIN GPIO_Pin_1
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY1_GPIO_PORT GPIOC
#define KEY1_GPIO_PIN GPIO_Pin_13
#define digitalTOGGLE(p,i) {p->ODR ^= i ;}
//C语言知识点, ^ :两个数相 ^,相同的话为0,不同的话为1
#define LED0_TOGGLE digitalTOGGLE(LED0_GPIO_PORT,LED0_GPIO_PIN)
#define LED1_TOGGLE digitalTOGGLE(LED1_GPIO_PORT,LED1_GPIO_PIN)
void KEY_GPIO_Config(void);
uint8_t KEY_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
/*无锁存,按下亮*/
void KEY_ON_OFF(void);
/*有锁存*/
void KEY_Delay(void);
#endif /*__BSP_KEY_H*/
按键状态读取
有两种形式。
无锁存:按下就亮,松开就灭
有锁存:按一下亮,再按一下灭
无锁存
tips:if 语句里面调用了一个固件库函数 GPIO_ReadInputDataBit,也就是读取一个 I/O 端口的电平输入状态。此函数中有两个参数:第一个参数是使用哪个 I/O 端口组,第二个参数是端口号。那么 if 语句判断此 I/O 端口状态,如果读取到高电平(逻辑 1),表示 if 判断为真,则执行LED_OFF,把 LED 的 I/O 端口变成高电平,LED 熄灭;如果读取到低电平(逻辑 0),if 判断为假,则执行 else 里面的程序 LED_ON,将 LED 点亮。
void KEY_ON_OFF(void)
{
/*第一个方法*/
// if(GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN))/*读取按键的电平,返回值为输入端口管脚值,读取高电平为真,低电平为假*/
// {
// LED0_OFF;
}else{
LED0_ON;
// }
/*新的方法*/
GPIO_WriteBit(LED0_GPIO_PORT,LED0_GPIO_PIN,(BitAction)(GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN)));/*没有按下是高电平,灯不亮*/
}
有锁存
/*有锁存*/
void KEY_Delay(void)
{
if(!GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN))/*读取按键的接口电平*/
{
Delay_ms(20);/*去抖动,我的按键卡死在消抖这里,删掉就可运行*/
if(!GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN))/*再次读取按键的接口电平*/
{
GPIO_WriteBit(LED0_GPIO_PORT,LED0_GPIO_PIN,(BitAction)(1-GPIO_ReadOutputDataBit(LED0_GPIO_PORT,LED0_GPIO_PIN)));/*LED取反*/
while(!GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN));/*判断按键是否松开*/
}
}
}
/*
//STM32标准库提供了库函数GPIO_ReadInputDataBit来获取位状态
//该函数输入GPIO端口及引脚号
//函数返回该引脚的电平状态
//高电平返回1
//低电平返回0
*/
tips:先判断按键是否被按下,如果被按下则延时 20ms,再一次判断是否被按下,如果被按下则进行相对应的 LED 控制,最后等待按键被放开,只有按键被放开才退出程序。
(这段程序我没运行成功,疑惑,以后再说)
二进制控制灯的亮灭
使用的是PA0,PA1,高电平才会亮灯
GPIO初始化
#include "bsp_led.h"
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitTStruct;
RCC_APB2PeriphClockCmd(GPIOA , ENABLE);
GPIO_InitTStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitTStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitTStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED0_GPIO_PORT,&GPIO_InitTStruct);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
}
具体的程序
void KEY_CON(void)
{
static uint8_t a;
if(!GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN)){
Delay_ms(20); /*我用了延时,程序动不了,你可以继续用*/
if(!GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN)){
a++;
if(a>3)
{
a=0;
}
GPIO_Write(GPIOA ,a);
while(!GPIO_ReadInputDataBit(KEY0_GPIO_PORT,KEY0_GPIO_PIN));
}
}
}
tips:定义的一个 8 位无符号变量。当按键确定被按下之后,便执行a++,也就是 a 的值加 1。初始状态下,变量 a 的值为 0,加 1 后为 1。下边使用了 GPIO_Write(GPIO,a),写入整组 I/O端口状态,写入的是 LEDPORT,也就是 PB 组端口,写入的值是变量 a,a 的值此时是 1,使得整组 16个 I/O 端口组中第 0 位(PA0)为 1,即 LED1 的状态为高电平点亮。当下一次按键被按下时,a 值又加1 等于 2,则 LED2 点亮;当下一次按键被按下时,a 再加 1 等于 3,在二进制数中,3 是 11,LED1 和LED2 同时点亮;当 a 值加到 4 时, if 语句发挥作用,判断 a 的值大于 3 则让 a 的值等于 0,再次写入就使得两个 LED 同时熄灭,也就实现了刚才实验的效果。
因为我没有加按键消抖,所以程序容易识别错误。
实现的视频
二进制点亮的效果