第一節 跑馬燈實驗
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);}
}
?