STM32F103C8T6 学习笔记摘要(三)
第一节 跑马灯实验
1. 了解电路
结构图
说明一下:
- 那几个LED的引脚线和数码管的是一样的,如果不想让LED亮,就可以把J11的接线帽拔了
- 这里的引脚是PA0-PA7
原理图
说明一下:
- 当J11接线帽盖上时,VCC3.3_LED就会有一个正电压
- 而我们最终要实现跑马灯效果时,就是指定对应PA0-PA7的引脚上输入低电平,就可以了
2. 初始化LED
Led.h
#ifndef _H_LED
#define _H_LED
#include "stm32f10x.h"
#include "system.h"// 初始化LED
void led_init(void);#endif
Led.c
#include "Led.h"// 封装点亮LED的前2步
void led_init(void)
{//step1: 给APB GPIOA 时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//step2:将引脚的工作模式 设置为推挽输出50MHZGPIO_InitTypeDef initDef;initDef.GPIO_Speed = GPIO_Speed_50MHz;initDef.GPIO_Mode = GPIO_Mode_Out_PP;// PA0initDef.GPIO_Pin = GPIO_Pin_0;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_0);// 设置为高电平-对应LED不亮// PA1initDef.GPIO_Pin = GPIO_Pin_1;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_1);// 设置为高电平-对应LED不亮// PA2initDef.GPIO_Pin = GPIO_Pin_2;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_2);// 设置为高电平-对应LED不亮// PA3initDef.GPIO_Pin = GPIO_Pin_3;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_3);// 设置为高电平-对应LED不亮// PA4initDef.GPIO_Pin = GPIO_Pin_4;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_4);// 设置为高电平-对应LED不亮// PA5initDef.GPIO_Pin = GPIO_Pin_5;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_5);// 设置为高电平-对应LED不亮// PA6initDef.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_6);// 设置为高电平-对应LED不亮// PA7initDef.GPIO_Pin = GPIO_Pin_7;GPIO_Init(GPIOA,&initDef);GPIO_SetBits(GPIOA,GPIO_Pin_7);// 设置为高电平-对应LED不亮
}
3. ledWriteData函数
led.h
#ifndef _H_LED
#define _H_LED
#include "stm32f10x.h"
#include "system.h"// 初始化LED
void led_init(void);// 对应PA0-7设置为低电平 其他引脚为高电平
void ledWriteData(u8 data);#endif
led.c
// 对应PA0-7设置为低电平 其他引脚为高电平
void ledWriteData(u8 data)
{// data=1111 1110(第1个led)for(u8 i = 0;i < 8;i++){if(data & 0x01){ // GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_SET);// 设置高电平}else{GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_RESET);// 设置低电平}data = data >> 1;// data = 0111 111(第8个led)}
}
说明一下:
- 首先GPIO_Pin_0 到GPIO_Pin_n的地址是有规律的,就是GPIO_Pin_n = GPIO_Pin_0 << n
- GPIO_Pin_0<<i 表示GPIO_Pin_0 到GPIO_PIn_7
第一次循环:将第1个led设置为低电平
- data & 0x01 表示1111 1110 & 0000 0001 = 0
则进入:GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_RESET);// 设置低电平 - data = data >> 1;// data = 0111 111(第8个led)
第二次循环:将第8个led设置为高电平
- data & 0x01 表示0111 1111 & 0000 0001 = 1
则进入:GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_SET);// 设置高电平 - data = data >> 1;// data = 1011 1111(第7个led)
依次类推,第7个 第6个led都将设置为高电平
4. main函数
#include "stm32f10x.h"
#include "Led.h"
#include "systick.h"
int main()
{SysTick_Init(72);//1.初始化ledled_init();u8 i = 0;while(1){// 设置第1个为低电平 其他为高电平ledWriteData(~(0x01<<i));i++;if(i>7){i=0;}// 定时delay_ms(100);}//return 0;
}
第二节 蜂鸣器实验
1. 了解电路
结构图
原理图
2. beep.h
#ifndef _H_BEEP
#define _H_BEEP
#include "stm32f10x.h"
#include "system.h"
#include "systick.h"// 位带操作-PB0
#define BEEP PBout(0)// 初始化LED
void beep_init(void);// time持续时间 us延迟时间
void beep_active(u16 time,u8 us);#endif
说明一下:
- #define BEEP PBout(0),这里使用了位带操作,方便后面将其设置为低电平
3. beep.c
#include "beep.h"// 初始化LED
void beep_init(void)
{// PB0// 开启时钟始能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 设置工作模式GPIO_InitTypeDef initdef;initdef.GPIO_Speed = GPIO_Speed_50MHz;initdef.GPIO_Pin = GPIO_Pin_0;initdef.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOB,&initdef);// 设置为高电平GPIO_SetBits(GPIOB,GPIO_Pin_0);}// time持续时间 us延迟时间
void beep_active(u16 time,u8 us)
{while(time--){BEEP=!BEEP;delay_us(us);}
}
4. main.c
#include "stm32f10x.h"
#include "beep.h"
#include "systick.h"int main()
{SysTick_Init(72);// 1. 初始化蜂鸣器beep_init();while(1){// 2. 活跃蜂鸣器beep_active(100,100);delay_ms(200);}
}
第三节 数码管实验
1. 数码管简介
数码管是一种半导体发光器件,其基本单元是发光二极管。数码管也称LED数码管,不同行业人士对数码管的称呼不一样,其实都是同样的产品。数码管按段数可分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管单元,也就是多一个小数点(DP),这个小数点可以更精确的表示数码管想要显示的内容;按能显示多少个(8)可分为 1 位、 2 位、3 位、4 位、5位、6 位、7 位等数码管。按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管。
2. 数码管显示原理
共阴极时,高电平,亮,而共阳极反之
从上图可看出,一位数码管的引脚是 10 个,显示一个8 字需要7 个小段,另外还有一个小数点,所以其内部一共有 8 个小的发光二极管,最后还有一个公共端,多数生产商为了封装统一,单位数码管都封装10 个引脚,其中第3和第 8 引脚是连接在一起的。而它们的公共端又可分为共阳极和共阴极,图中间为共阳极内部原理图,右图为共阴极内部原理图。
3. 了解电路
结构图
原理图
说明一下:
- 对于PB3 PB4 PB5 来说需要关闭调试,才能当做基础的GPIO口使用
4. 关于74HC245芯片
芯片74HC245 是一种三态输出、八路信号收发器,主要应用于大屏显示,以及其它的消费类电子产品中增加驱动。
5. 简单总结
- 位选三条线(LSA、LSB、LSC)PB5、4、3
- 段选8条线 PA0 - PA7
6. smg.h
#ifndef _H_SMG
#define _H_SMG
#include "stm32f10x.h"
#include "system.h"
#define LSA PBout(5)
#define LSB PBout(4)
#define LSC PBout(3)// 初始化smg
void smg_init(void);// 设置低电平
void smgWriteData(u8 data);#endif
7. smg.c
smg_init函数
void smg_init(void)
{//1.给APB2 GPIOA GPIOB 时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 关闭PB3 PB4 PB5调试,使其能当作正常GPIO口使用RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);//2. 设置工作模式GPIO_InitTypeDef initDef;initDef.GPIO_Speed= GPIO_Speed_50MHz;initDef.GPIO_Mode = GPIO_Mode_Out_PP;//PA0-PA7initDef.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_Init(GPIOA,&initDef); //PB3-PB5initDef.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_4 | GPIO_Pin_3;GPIO_Init(GPIOB,&initDef);
}
smgWriteData函数
void smgWriteData(u8 data)
{// data=1111 1110(第1个led)for(u8 i = 0;i < 8;i++){if(data & 0x01){ // GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_SET);// 设置高电平}else{GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_RESET);// 设置低电平}data = data >> 1;// data = 0111 111(第8个led)}
}
8. main.c
#include "stm32f10x.h"
#include "smg.h"
#include "systick.h"u8 gsmg_code[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};int main()
{SysTick_Init(72);// 1. 初始化蜂鸣器smg_init();while(1){// 在那个位置上显示 - 位选LSC = 0;LSB = 0;LSA = 1;// 在对应的位置上显示什么内容 - 段选for(u8 i = 0;i < 10;i++){ smgWriteData(gsmg_code[i]);delay_ms(1000);}}
}
第四节 独立按键实验-默认是高电平
按键是一种电子开关,使用时轻轻按开关按钮就可使开关接通,当松开手时,开关断开。我们开发板上使用的按键及内部简易图如下图所示
按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态,如果按键按下,初始导通状态变为断开,初始断开状态变为导通。通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,电压信号如下图所示:
由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的,一般为 5ms 到 10ms。按键稳定闭合时间的长短则由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起按键被误读多次。为了确保 CPU 对按键的一次闭合仅作一次处理,必须进行消抖。
按键消抖有两种方式,一种是硬件消抖,另一种是软件消抖。为了使电路更加简单,通常采用软件消抖。我们开发板也是采用软件消抖,一般来说一个简单的按键消抖就是先读取按键的状态,如果得到按键按下之后,延时10ms,再次读取按键的状态,如果按键还是按下状态,那么说明按键已经按下。其中延时10ms 就是软件消抖处理,至于硬件消抖,大家可以百度了解下,网上都有非常详细的介绍
1. 了解电路
结构图
原理图
说明一下:
- 默认是高电平,按下按钮之后才能变成低电平
2. key初始化
这个板子上有4个按键,我想让它控制对应的led0 led1 led2 led3 led4
key.h
#ifndef _H_KEY
#define _H_KEY
#include "stm32f10x.h"
#include "system.h"
#include "systick.h"// 位带操作
#define KEY1 PAin(15)
#define KEY2 PAin(14)
#define KEY3 PAin(13)
#define KEY4 PAin(12)// 初始化按键
void key_init(void);// 检查按键状态
u8 keyScan(u8 mode);#endif
key_init函数
#include "key.h"// 初始化按键
void key_init(void){// 1gpio时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能是GPIOARCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 使能 AFIO 时钟// 这几根线上也有调试功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);// 关闭JTAG 和 SWD 功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE); // 关闭JTAG 和 SWD 功能// 设置工作模式GPIO_InitTypeDef initDef;initDef.GPIO_Mode = GPIO_Mode_IPU;// 上拉输入模式initDef.GPIO_Speed = GPIO_Speed_50MHz;initDef.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOA,&initDef);initDef.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOA,&initDef);initDef.GPIO_Pin = GPIO_Pin_14;GPIO_Init(GPIOA,&initDef);initDef.GPIO_Pin = GPIO_Pin_15;GPIO_Init(GPIOA,&initDef);
}
说明一下:
- PA12-PA15这几根线上也有调式功能,初始化时需要关闭
- 注意:它的工作模式应该设置为上拉输入
led.h
3. key.c
#include "key.h"// 初始化按键
void key_init(void){// 1gpio时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能是GPIOARCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 使能 AFIO 时钟// 这几根线上也有调试功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);// 关闭JTAG 和 SWD 功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE); // 关闭JTAG 和 SWD 功能// 设置工作模式GPIO_InitTypeDef initDef;initDef.GPIO_Mode = GPIO_Mode_IPU;// 上拉输入模式initDef.GPIO_Speed = GPIO_Speed_50MHz;initDef.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOA,&initDef);initDef.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOA,&initDef);initDef.GPIO_Pin = GPIO_Pin_14;GPIO_Init(GPIOA,&initDef);initDef.GPIO_Pin = GPIO_Pin_15;GPIO_Init(GPIOA,&initDef);
}// 检查按键状态
u8 keyScan(u8 mode)
{static u8 flag = 1;if(mode) flag = 1;// 持续检测-长按按键出现闪烁效果if(flag && (KEY1 == 0 ||KEY2 == 0 || KEY3 == 0 || KEY4 == 0)) {delay_ms(10);// 消抖flag = 0;// 返回值:表示确定是那个按键被按下,切换对应的LED状态if(KEY1 == 0) return 1;if(KEY2 == 0) return 2;if(KEY3 == 0) return 3;if(KEY4 == 0) return 4;}// 此时所有按键都没有被按下else if(KEY1 == 1 && KEY2 == 1 && KEY3 == 1 && KEY4 == 1){flag = 1;}else{return 0;}
}
说明一下
- 当mode = 1表示长按按键时出现闪烁效果
- 当mode = 0表示长按按键出现常亮效果
4. main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "systick.h"int main()
{// 滴答定时器SysTick_Init(72);// 初始化led_init();key_init();u8 i = 0;while(1){i = keyScan(0);if(i == 1) LED1 = !LED1;else if(i == 2) LED2 = !LED2;else if(i == 3) LED3 = !LED3;else if(i == 4) LED4 = !LED4;delay_ms(150);}
}
第五节. 矩阵按键实验
1. 了解电路
结构图
说明一下:
- 我们要做的是让前2排按键点亮led1-led8
- 后两排要做的是点灭led1-led8
原理图
2. key.h
我们要做的是让前2排按键点亮led1-led8,后两排要做的是点灭led1-led8
Led.h
key.h
#ifndef _H_KEY
#define _H_KEY
#include "stm32f10x.h"
#include "system.h"
#include "systick.h"// 四根行线
#define KEY_H1_PIN GPIO_Pin_8
#define KEY_H2_PIN GPIO_Pin_9
#define KEY_H3_PIN GPIO_Pin_10
#define KEY_H4_PIN GPIO_Pin_11// 四根列线
#define KEY_L1_PIN GPIO_Pin_12
#define KEY_L2_PIN GPIO_Pin_13
#define KEY_L3_PIN GPIO_Pin_14
#define KEY_L4_PIN GPIO_Pin_15// 初始化按键
void key_init(void);// 检查按键状态
u8 keyScan(void);#endif
3. key_init函数
#include "key.h"// 初始化按键
void key_init(void){// 1gpio时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能是GPIOARCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 使能 AFIO 时钟// 这几根线上也有调试功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);// 关闭JTAG 和 SWD 功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE); // 关闭JTAG 和 SWD 功能// 四根行线设置为下拉输入 - 设置工作模式GPIO_InitTypeDef initDef;initDef.GPIO_Speed = GPIO_Speed_50MHz;initDef.GPIO_Mode = GPIO_Mode_IPD;// 下拉输入模式initDef.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_14;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_15;GPIO_Init(GPIOB,&initDef);// 四根列线设置为推挽输出 - 设置工作模式initDef.GPIO_Mode = GPIO_Mode_Out_PP;// 推挽输出模式initDef.GPIO_Pin = GPIO_Pin_8;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_9;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_10;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_11;GPIO_Init(GPIOB,&initDef);}
说明一下:
- 四根行线设置为下拉输入 - 设置工作模式
- 四根列线设置为推挽输出 - 设置工作模式
4. keyScan函数(有bug)
GPIO_ReadInputDataBit - 读取指定端口管脚的输入
keyScan函数
// 检查按键状态
u8 keyScan()
{// 设置前四行为高电平GPIO_SetBits(GPIOB,KEY_H1_PIN);GPIO_SetBits(GPIOB,KEY_H2_PIN);GPIO_SetBits(GPIOB,KEY_H3_PIN);GPIO_SetBits(GPIOB,KEY_H4_PIN);// 读取指定端口管脚的输入if((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) == 0){// 没有按键被按下return 0;}else{// 等待一会儿 再读取// 读取指定端口管脚的输入if((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) == 0){// 没有按键被按下return 0;}}}
keyScan函数
// 检查按键状态
u8 keyScan()
{// 设置前四行为高电平GPIO_SetBits(GPIOB,KEY_H1_PIN);GPIO_SetBits(GPIOB,KEY_H2_PIN);GPIO_SetBits(GPIOB,KEY_H3_PIN);GPIO_SetBits(GPIOB,KEY_H4_PIN);// 读取指定端口管脚的输入if((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) == 0){// 没有按键被按下return 0;}else{// 等待一会儿 再读取// 读取指定端口管脚的输入if((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) == 0){// 没有按键被按下return 0;}}// 检测是否有按键被按下u8 col1,col2,col3,col4;// 第一行:设置第一行为低电平 其他为高电平GPIO_SetBits(GPIOB,KEY_H1_PIN);GPIO_ResetBits(GPIOB,KEY_H2_PIN);GPIO_ResetBits(GPIOB,KEY_H3_PIN);GPIO_ResetBits(GPIOB,KEY_H4_PIN);// 读取四列的状态 col1 = GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN);col2 = GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN);col3 = GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN);col4 = GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN);if(col1 == 1 && col2 == 0&& col3==0 && col4 == 0) return 1;if(col1 == 0 && col2 == 1&& col3==0 && col4 == 0) return 2;if(col1 == 0 && col2 == 0&& col3==1 && col4 == 0) return 3;if(col1 == 0 && col2 == 0&& col3==0 && col4 == 1) return 4;while((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) > 0);// 第二行:设置第二行为低电平 其他为高电平GPIO_ResetBits(GPIOB,KEY_H1_PIN);GPIO_SetBits(GPIOB,KEY_H2_PIN);GPIO_ResetBits(GPIOB,KEY_H3_PIN);GPIO_ResetBits(GPIOB,KEY_H4_PIN);// 读取四列的状态 col1 = GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN);col2 = GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN);col3 = GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN);col4 = GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN);if(col1 == 1 && col2 == 0&& col3==0 && col4 == 0) return 5;if(col1 == 0 && col2 == 1&& col3==0 && col4 == 0) return 6;if(col1 == 0 && col2 == 0&& col3==1 && col4 == 0) return 7;if(col1 == 0 && col2 == 0&& col3==0 && col4 == 1) return 8;while((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) > 0);// 第三行:设置第三行为低电平 其他为高电平GPIO_ResetBits(GPIOB,KEY_H1_PIN);GPIO_ResetBits(GPIOB,KEY_H2_PIN);GPIO_SetBits(GPIOB,KEY_H3_PIN);GPIO_ResetBits(GPIOB,KEY_H4_PIN);// 读取四列的状态 col1 = GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN);col2 = GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN);col3 = GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN);col4 = GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN);if(col1 == 1 && col2 == 0&& col3==0 && col4 == 0) return 9;if(col1 == 0 && col2 == 1&& col3==0 && col4 == 0) return 10;if(col1 == 0 && col2 == 0&& col3==1 && col4 == 0) return 11;if(col1 == 0 && col2 == 0&& col3==0 && col4 == 1) return 12;while((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) > 0);// 第四行:设置第四行为低电平 其他为高电平GPIO_ResetBits(GPIOB,KEY_H1_PIN);GPIO_ResetBits(GPIOB,KEY_H2_PIN);GPIO_ResetBits(GPIOB,KEY_H3_PIN);GPIO_SetBits(GPIOB,KEY_H4_PIN);// 读取四列的状态 col1 = GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN);col2 = GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN);col3 = GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN);col4 = GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN);if(col1 == 1 && col2 == 0&& col3==0 && col4 == 0) return 13;if(col1 == 0 && col2 == 1&& col3==0 && col4 == 0) return 14;if(col1 == 0 && col2 == 0&& col3==1 && col4 == 0) return 15;if(col1 == 0 && col2 == 0&& col3==0 && col4 == 1) return 16;while((GPIO_ReadInputDataBit(GPIOB,KEY_L1_PIN)) | (GPIO_ReadInputDataBit(GPIOB,KEY_L2_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L3_PIN))| (GPIO_ReadInputDataBit(GPIOB,KEY_L4_PIN)) > 0);return 0;}
5. main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "systick.h"int main()
{// 滴答定时器SysTick_Init(72);// 初始化led_init();key_init();u8 i = 0;while(1){i = keyScan();// 前2排按键按下 点亮LEDif(i == 1) LED1 = 0;else if(i == 2) LED2 = 0;else if(i == 3) LED3 = 0;else if(i == 4) LED4 = 0;else if(i == 5) LED5 = 0;else if(i == 6) LED6 = 0;else if(i == 7) LED7 = 0;else if(i == 8) LED8 = 0;// 后2排按键按下 点灭LEDelse if(i == 9) LED1 = 1;else if(i == 10) LED2 = 1;else if(i == 11) LED3 = 1;else if(i == 12) LED4 = 1;else if(i == 13) LED5 = 1;else if(i == 14) LED6 = 1;else if(i == 15) LED7 = 1;else if(i == 16) LED8 = 1;delay_ms(150);}
}
第六节 点阵实验
1. 了解电路
结构图
原理图
说明一下:
- 当需要串联其他点阵时,就需要使用VCC3.3和9号线
2. 74HC595芯片介绍
点阵 LED 基本结构
-
右侧(ROW1-ROW8) 控制的是行(阳极/正极)。
-
下侧(SMG A ~ SMG DP) 控制的是列(阴极/负极)
74HC595 是一个 8 位串行转并行移位寄存器,它的作用是:
- 将你通过
SER
引脚串行输入的 8 位数据,在SRCLK
(移位时钟)作用下依次移入。 -
当你触发
RCLK
(锁存时钟)的 上升沿 时,数据会被输出到 QA~QH(即点阵的行控制)。
点阵工作流程(动态扫描)
- 行只有设置为高电平有效 列只有设置为低电平有效 则我们点亮第 1 行第 1 列的 LED(即 A1-K1 对应 LED):
- 行为0x08 列为0x7F
关键信号控制如下:
工作原理
- 采用高位的输入 数据到 移位寄存器中
3. dzled.h
#ifndef _H_DZLED
#define _H_DZLED
#include "stm32f10x.h"
#include "system.h"
#include "systick.h"// 三个总要信号 - 产生上升沿
#define SER PBout(4)
#define RCLK PBout(5)
#define SRCLK PBout(3)// 初始化点阵
void dzled_init(void);// 行输入
void writeRowData(u8 data);// 列输入
void writeColData(u8 data);#endif
4. dzled.c
dzled_init函数
// 初始化点阵
void dzled_init(void)
{//1.给APB2 GPIOA GPIOB 时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 关闭PB3 PB4 PB5调试,使其能当作正常GPIO口使用RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);//2. 设置工作模式GPIO_InitTypeDef initDef;initDef.GPIO_Speed= GPIO_Speed_50MHz;initDef.GPIO_Mode = GPIO_Mode_Out_PP;//PA0-PA7initDef.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_Init(GPIOA,&initDef); //PB3-PB5initDef.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_4 | GPIO_Pin_3;GPIO_Init(GPIOB,&initDef);}
说明一下:
- 由于数码管和点阵的使用的同一根线,则初始化时直接拷贝过来就行了
writeColData函数
// 列输入
void writeColData(u8 data)
{// data=1111 1110(第1个led)for(u8 i = 0;i < 8;i++){if(data & 0x01){ // GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_SET);// 设置高电平}else{GPIO_WriteBit(GPIOA,GPIO_Pin_0<<i,Bit_RESET);// 设置低电平}data = data >> 1;// data = 0111 111(第8个led)}
}
说明一下:
- 点阵的列输入又和跑马灯的程序一致,则直接拷贝过来就行了
writeRowData函数
// 行输入
void writeRowData(u8 data)
{for(u8 i = 0;i < 8;i++){// 1.数据从高位依次输入SER = data>>7;// 每一次得到数据的最高位// 移位寄存器的时钟线变化SRCLK = 0;delay_us(1);SRCLK = 1;//上升沿信号 移位data <<= 1;// 把已经输入的数据丢弃}// 把数据从移位寄存器 移动到 存储寄存器里面RCLK = 0;delay_us(1);RCLK = 1;// 给一个上升沿信号
}
5. main.c
#include "stm32f10x.h"
#include "led.h"
#include "dzled.h"
#include "systick.h"int main()
{// 滴答定时器SysTick_Init(72);// 初始化dzled_init();// 点亮最左上角的LEDwriteRowData(0x80);// 行只有设置高电平有效writeColData(0x7F);// 列只有设置低电平有效u8 i = 0;while(1){delay_ms(150);}
}
说明一下:
- 直接调用列输入 行输入就行了
第七节 动力模块->直流电机
直流电机是指能将直流电能转换成机械能(直流电动机)或将机械能转换成直流电能(直流发电机)的旋转电机
直流电机的结构应由定子和转子两大部分组成,直流电机没有正负之分,在两端加上直流电就能工作
开发板配置的直流电机为 5V 直流电机,其主要参数如下:轴长:8mm 轴径:2mm 电压:1-6V 参考电流:0.35-0.4A 3V 转速:17000-18000 转每分钟 外观实物图如下:
1. ULN2003 芯片
单片机主要是用来控制而非驱动,如果直接使用芯片的GPIO管脚去驱动大功率器件,要么将芯片烧坏,要么就驱动不起来。所以要驱动大功率器件,比如电机。就必须搭建驱动电路,开发板上板载的驱动芯片是ULN2003,该芯片是一个单片高电压、高电流的达林顿晶体管阵列集成电路。不仅可以用来驱动直流电机,还可用来驱动五线四相步进电机,比如28BYJ48 步进电机。
2. 了解电路
简单来说:使用PB8就行了
结构图
原理图
3. moto.h
#ifndef _H_MOTO
#define _H_MOTO
#include "stm32f10x.h"
#include "system.h"
#include "systick.h"
// PB8的引脚 - 位带操作
#define MOTO PBout(8)// 初始化电机
void moto_init(void);#endif
4. moto.c
#include "moto.h"// 初始化电机
void moto_init(void)
{// 1. 时钟始能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 2.关闭调试功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);// 关闭JTAGGPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);// 3. 初始化GPIO_InitTypeDef initDef;// 结构体initDef.GPIO_Mode = GPIO_Mode_Out_PP;// 推挽输出initDef.GPIO_Speed = GPIO_Speed_50MHz;initDef.GPIO_Pin = GPIO_Pin_8;GPIO_Init(GPIOB,&initDef);GPIO_SetBits(GPIOB,GPIO_Pin_8);// 设置为高电平}
5. main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "moto.h"
#include "systick.h"int main()
{// 滴答定时器SysTick_Init(72);// 初始化led_init();moto_init();while(1){// 检测矩阵按键中: 那个按键被按下u8 i = keyScan();if(i == 1){// 高低电平之间切换LED1 = !LED1;MOTO = !MOTO;}delay_ms(150);}
}
说明一下:
- 最终效果:点击矩阵按键上的第一个按键,就可控制第一个led的亮灭和电机的启动
第八节 步进电机
步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件
工作原理
单极性
双极性
2.1 28BYJ-48 步进电机简介
28BYJ48 步进电机自带减速器,为五线四相单极性步进电机,直径为28mm,
28BYJ48 电机内部结构等效图如下所示:
28BYJ48 步进电机主要参数如下所示:
在上图中 28BYJ48 步进电机主要参数中可以看到有一个减速比:1:64,步进角为 5.625/64 度,如果需要转动一圈,那么需要 360/5.625*64=4096 个脉冲信号。 减速比这个和之前介绍的直流减速电机有点类似,所以28BYJ48 步进电机实际上是:减速齿轮+步进电机组成,28BYJ48 步进电机减速齿轮实物图如下所示:
减速齿轮计算方法如下所示:
2.2 了解电路
1、MOTO IN1-4全部用到,所以端口是PB8-9,PB12-13
2、根据原理了解,需要实现的是电位的切换驱动旋转
3、给一个高电平,输出低电平,形成电流环路,产生磁场
结构图
原理图
2.3 moto.h
#ifndef _H_MOTO
#define _H_MOTO
#include "stm32f10x.h"
#include "system.h"
#include "systick.h"
// 位带操作
#define MOTOR_IN1 PBout(8)
#define MOTOR_IN2 PBout(9)
#define MOTOR_IN3 PBout(12)
#define MOTOR_IN4 PBout(13)// 初始化步进电机
void moto_init(void);// 运行步进电机
void motor_run(u8 step,u8 dir,u8 speed,u16 angle,u8 sta);
#endif
参数说明:
- step表示指定步进控制节拍,可选值4或8
- dir表示方向选择,其中1表示顺时针,0表示逆时针
- speed表示速度,可选范围为1-5
- angle表示角度,可选范围为0-360
- sta表示运行状态,其中1启动,0停止
2.4 moto.c
#include "moto.h"// 初始化电机
void moto_init(void)
{// 1. 时钟始能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 2.关闭调试功能GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);// 关闭JTAGGPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);// 3. 初始化GPIO_InitTypeDef initDef;// 结构体initDef.GPIO_Mode = GPIO_Mode_Out_PP;// 推挽输出initDef.GPIO_Speed = GPIO_Speed_50MHz;initDef.GPIO_Pin = GPIO_Pin_8;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_9;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOB,&initDef);initDef.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOB,&initDef);
}// 运行步进电机
void motor_run(u8 step,u8 dir,u8 speed,u16 angle,u8 sta)
{// 启动if(sta){// 顺if(dir){// 转动角度所对应的信号个数for(u8 j = 0;j < angle*64/45;j++){// 把整个一圈分为4 / 8节拍// 每个节拍应该怎么转动 电机应该是什么状态for(int i = 0;i < 8;i+=(8/step)){switch (i){case 0: MOTOR_IN1=1;MOTOR_IN2=0;MOTOR_IN3=0;MOTOR_IN4=0;break;case 1: MOTOR_IN1=1;MOTOR_IN2=1;MOTOR_IN3=0;MOTOR_IN4=0;break;case 2: MOTOR_IN1=0;MOTOR_IN2=1;MOTOR_IN3=0;MOTOR_IN4=0;break;case 3: MOTOR_IN1=0;MOTOR_IN2=1;MOTOR_IN3=1;MOTOR_IN4=0;break;case 4: MOTOR_IN1=0;MOTOR_IN2=0;MOTOR_IN3=1;MOTOR_IN4=0;break;case 5: MOTOR_IN1=0;MOTOR_IN2=0;MOTOR_IN3=1;MOTOR_IN4=1;break;case 6: MOTOR_IN1=0;MOTOR_IN2=0;MOTOR_IN3=0;MOTOR_IN4=1;break;case 7: MOTOR_IN1=1;MOTOR_IN2=0;MOTOR_IN3=0;MOTOR_IN4=1;break;}delay_ms(speed);}}}else{// 逆时针// 转动角度所对应的信号个数for(u8 j = 0;j < angle*64/45;j++){// 把整个一圈分为4 / 8节拍// 每个节拍应该怎么转动 电机应该是什么状态for(int i = 0;i < 8;i+=(8/step)){switch (i){case 0: MOTOR_IN1=1;MOTOR_IN2=0;MOTOR_IN3=0;MOTOR_IN4=1;break;case 1: MOTOR_IN1=0;MOTOR_IN2=0;MOTOR_IN3=0;MOTOR_IN4=1;break;case 2: MOTOR_IN1=0;MOTOR_IN2=0;MOTOR_IN3=1;MOTOR_IN4=1;break;case 3: MOTOR_IN1=0;MOTOR_IN2=0;MOTOR_IN3=1;MOTOR_IN4=0;break;case 4: MOTOR_IN1=0;MOTOR_IN2=1;MOTOR_IN3=1;MOTOR_IN4=0;break;case 5: MOTOR_IN1=0;MOTOR_IN2=1;MOTOR_IN3=0;MOTOR_IN4=0;break;case 6: MOTOR_IN1=1;MOTOR_IN2=1;MOTOR_IN3=0;MOTOR_IN4=0;break;case 7: MOTOR_IN1=1;MOTOR_IN2=0;MOTOR_IN3=0;MOTOR_IN4=0;break;}delay_ms(speed);}}}}
}
2.5 main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "moto.h"
#include "systick.h"int main()
{// 滴答定时器SysTick_Init(72);// 初始化led_init();moto_init();u8 dir = 1;u8 sta = 0;u8 speed = 1;// 取值1-5while(1){// 检测矩阵按键中: 那个按键被按下u8 i = keyScan();if(i == 1){sta = !sta;// 启动/停止}else if(i == 2){dir = !dir;// 正转/反转}else if(i == 3){// 加速度if(speed > 1) speed--;}else if(i == 4){// 减sudoif(speed<5) speed++;}else{delay_ms(10);}motor_run(8,dir,speed,1,sta);delay_ms(150);}
}