前言
编程语言、硬件电路、软件编程、蜂鸣器驱动程序、MIDI音乐播放程序
目录
一、单片机编程语言
二、核心板电路原理图
- C6、C7晶振的起振电容
- 继电器作为整个电源的电器开关
- 用户通过PB0、PB1操纵灯的开关
- FT232串口将USB接口转为TTL电平USART串口来给单片机ISP下载,也可以与电脑开发
- 3.3V稳压芯片将USB电源中5V电压转为3.3V,来给单片机以及周边使用3.3V的器件供电
- ASP控制芯片实现核心板程序自动下载
- C9、R7组成单片机外部的复位电路
- RTC备用电池是用来RTC不间断走时使用
三、 点亮LED灯
对IO端口初始化,程序控制IO端口电平变化
PB0输出高电平,LED点亮;低电平,LED熄灭
选择IO接口工作方式:
- GPIO_Mode_AIN 模拟输入
- GPIO_Mode_IN_FLOATING 浮空输入
- GPIO_Mode_IPD 下拉输入
- GPIO_Mode_IPU 上拉输入
- GPIO_Mode_Out_PP 推挽输出
- GPIO_Mode_Out_OD 开漏输出
- GPIO_Mode_AF_PP 复用推挽输出
- GPIO_Mode_AF_OD 复用开漏输出
设置IO接口速度:(2/10/50MHz)
推挽、上下拉、灌电流
GPIO库函数
GPIO_WriteBit
GPIO_ResetBits
清除端口位,低电平
GPIO_SetBits
置位端口位,高电平
GPIO_Write
给相应的端口写值
三种方法
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "led.h" int main (void){//主程序 RCC_Configuration(); //时钟设置 LED_Init(); //端口初始化 while(1){ //方法一 GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED1接口输出高电平,BitAction是枚举值 delay_s(1); //延时1秒 GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); delay_s(1); //延时1秒 //方法二 GPIO_SetBits(LEDPORT,LED1); //置位端口位,为高电平 delay_s(1); //延时1秒 GPIO_ResetBits(LEDPORT,LED1); //清除端口位,为低电平 delay_s(1); //方法三 GPIO_Write(LEDPORT,0x0001); //写入变量值 delay_s(1); //延时1秒 GPIO_Write(LEDPORT,0x0000); delay_s(1); } }
LED端口初始化
#include "led.h" void LED_Init(void){ //LED灯的接口初始化 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); //时钟上,启动APB2这条总线上功能 GPIO_InitStructure.GPIO_Pin = LED1 | LED2; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(LEDPORT, &GPIO_InitStructure); }
四、延时函数
用到滴答定时器
#include "delay.h" #define AHB_INPUT 72 //RCC中设置的AHB时钟频率填写到这里(单位MHz) void delay_us(u32 uS){ //uS微秒级延时程序(参考值即是延时数,72MHz时最大值233015) SysTick->LOAD=AHB_INPUT*uS; //重装计数初值(当主频是72MHz,72次为1微秒) SysTick->VAL=0x00; //清空定时器的计数器 SysTick->CTRL=0x00000005;//时钟源HCLK,打开定时器 while(!(SysTick->CTRL&0x00010000)); //等待计数到0 SysTick->CTRL=0x00000004;//关闭定时器 } void delay_ms(u16 ms){ //mS毫秒级延时程序(参考值即是延时数,最大值65535) while( ms-- != 0){ delay_us(1000); //调用1000微秒的延时 } } void delay_s(u16 s){ //S秒级延时程序(参考值即是延时数,最大值65535) while( s-- != 0){ delay_ms(1000); //调用1000毫秒的延时 } }
五、LED呼吸灯
其实就是调节占空比,即点亮时间和熄灭时间的比例,点亮时间越长,亮度越大
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "led.h" int main (void){//主程序 u8 menu; //标记变暗还是变亮模式,定义8位无符号变量a u16 t,i; //定义16位无符号变量a RCC_Configuration(); //时钟设置 LED_Init(); //初始化变量 menu = 0; t = 1; //延时值 while(1){ //呼吸灯 //变亮 if(menu == 0){ //同一个亮度增加了十倍 for(i = 1;i <= 10;i++){ GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED1接口输出高电平,BitAction是枚举值 delay_us(t); //延时1微秒 GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); delay_us(501-t); //延时1秒 } t++; if(t == 500){ menu = 1; //切换成变暗模式 } } if(menu == 1){ for(i = 1;i <=10;i++){ GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED1接口输出高电平,BitAction是枚举值 delay_us(t); //延时1微秒 GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); delay_us(501-t); //延时1秒 } t--; if(t==1){ menu = 0; //切换成变暗模式 } } } }
六、按键控制LED灯
即读出按下按键后,此端口的值,根据这个值给灯的端口相应的电平
按键端口设为上拉电阻
#include "key.h" void KEY_Init(void){ //微动开关的接口初始化 GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO的初始化枚举结构 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Pin = KEY1; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻 // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(KEYPORT,&GPIO_InitStructure); }
#include "sys.h" #define KEYPORT GPIOA //定义IO接口组 #define KEY1 GPIO_Pin_0 //定义IO接口 void KEY_Init(void);//初始化
补充:按键在按下的瞬间会有电平的抖动,若按下就读,电平可能有高有低,所以要加一个延时
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "led.h" #include "key.h" int main (void){//主程序 u8 a; RCC_Configuration(); //时钟设置 LED_Init(); KEY_Init(); a = 0; while(1){ //按键控制LED 方法一,无锁存,没有按下按键该按键端口为高电平,按下为低电平 if(GPIO_ReadInputDataBit(KEYPORT,KEY1)){ GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //给LED端口低电平 } else{ GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //高电平 } //方法二,无锁存 GPIO_WriteBit(LEDPORT,LED1,(BitAction)(!GPIO_ReadInputDataBit(KEYPORT,KEY1))); //方法三,有锁存,按键按下才执行 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ delay_ms(20); //延时去抖动 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1-GPIO_ReadOutputDataBit(LEDPORT,LED1))); //根据LED端口值取反 while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开,按键放开才跳出 ,防止在一直按下的情况,前面程序反复运行 } } //方法四,有锁存 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ delay_ms(20); //延时去抖动 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //两个LED显示2进制加法 a++; if(a>3){ a=0; } GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上) while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 } } } }
七、Flash读写程序
在上面点亮LED灯时,当断电后再次打开,LED熄灭,如果有了Flash读写功能,就可以再次打开后,LED仍亮起来
Flash库函数
#include "flash.h" //FLASH写入数据 void FLASH_W(u32 add,u16 dat){ //参数1:32位FLASH地址。参数2:16位数据 // RCC_HSICmd(ENABLE); //打开HSI时钟 这里已经使用外部时钟,无需内部时钟 FLASH_Unlock(); //解锁FLASH编程擦除控制器 FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);//清除标志位 FLASH_ErasePage(add); //擦除指定地址页,必须以页先擦除才可以写 FLASH_ProgramHalfWord(add,dat); //从指定页的addr地址开始写 FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);//清除标志位 FLASH_Lock(); //锁定FLASH编程擦除控制器 } //FLASH读出数据 u16 FLASH_R(u32 add){ //参数1:32位读出FLASH地址。返回值:16位数据 u16 a; a = *(u16*)(add);//从指定页的addr地址开始读 return a; }
地址
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "led.h" #include "key.h" #include "flash.h" #define FLASH_START_ADDR 0x0801f000 //写入的起始地址 int main (void){//主程序 u16 a; RCC_Configuration(); //时钟设置 LED_Init(); KEY_Init(); a = FLASH_R(FLASH_START_ADDR);//从指定页的地址读FLASH GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上) while(1){ //flash读写程序 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ delay_ms(20); //延时去抖动 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //两个LED显示2进制加法 a++; if(a>3){ a=0; } GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上) FLASH_W(FLASH_START_ADDR,a); //从指定页的地址写入FLASH while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 } } } }
注意:保持的临时数据要避免用户程序的地址,所以尽量选靠后地址
写入多个数据
FLASH操作注意事项
- 操作一定要先擦后写。
- 每页是1024个地址,起始地址0x08000000。
- 擦除操作以页为单位,写操作则必须以16位宽度为单位,允许跨页写入。
- STM32内置FLASH擦或写时,必须打开外部/内部高速振荡器
- FLASH可多次擦写10万次,不可死循环擦写。
- 擦写时要避开用户程序存储区的区域,否则会擦掉用户程序导致错误。
- 擦除一页要10ms(对于1k大小的一页),比较慢。而且不能单个字节的擦写。
八、蜂鸣器驱动程序
有源&无源蜂鸣器
外观几乎无区别,要通过型号辨别。
蜂鸣器部分电路图
PB5输入高电平,三极管C端和E端断开,BP1蜂鸣器断开。输入低电平,导通。但PB5不能长时间低电平,会导致蜂鸣器线圈长时间通电发热,导致损坏。所以需要保证PB5一般保持在高电平,因此加了一个上拉电阻。
频率周期的数量决定了蜂鸣器发声长度,每一个周期,高低电平占用时间,决定了蜂鸣器音调。频率越短声调越高。
#include "buzzer.h" #include "delay.h" void BUZZER_Init(void){ //蜂鸣器的接口初始化 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = BUZZER; //选择端口号 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(BUZZERPORT, &GPIO_InitStructure); GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(1)); //蜂鸣器接口输出高电平1 } void BUZZER_BEEP1(void){ //蜂鸣器响一声 u16 i; for(i=0;i<200;i++){ GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(0)); //蜂鸣器接口输出0 delay_us(500); //延时 GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(1)); //蜂鸣器接口输出高电平1 delay_us(500); //延时 } }
#ifndef __BUZZER_H #define __BUZZER_H #include "sys.h" #define BUZZERPORT GPIOB //定义IO接口 #define BUZZER GPIO_Pin_5 //定义IO接口 void BUZZER_Init(void);//初始化 void BUZZER_BEEP1(void);//响一声 #endif
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "led.h" #include "key.h" #include "buzzer.h" #include "flash.h" #define FLASH_START_ADDR 0x0801f000 //写入的起始地址 int main (void){//主程序 u16 a; RCC_Configuration(); //时钟设置 LED_Init(); KEY_Init(); BUZZER_Init(); BUZZER_BEEP1();//蜂鸣器音1 a = FLASH_R(FLASH_START_ADDR);//从指定页的地址读FLASH GPIO_Write(LEDPORT,a|0xfffc&GPIO_ReadOutputData(LEDPORT)); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上) while(1){ //蜂鸣器驱动程序 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ delay_ms(20); //延时去抖动 if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //两个LED显示2进制加法 a++; if(a>3){ a=0; } GPIO_Write(LEDPORT,a|0xfffc&GPIO_ReadOutputData(LEDPORT)); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上) BUZZER_BEEP1();//蜂鸣器音1 FLASH_W(FLASH_START_ADDR,a); //从指定页的地址写入FLASH while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 } } } }
注意这里一定是高电平为结束,防止蜂鸣器一直处于低电平状态。
这里得注意,因为蜂鸣器所连端口是PB组的,其中LEDPORT=GPIOB,GPIO_Write(LEDPORT,a)将除了两个灯的端口全部设为0,GPIO_ReadOutputData(LEDPORT)为读取整租IO端口电平状态。0xfffc&GPIO_ReadOutputData(LEDPORT)按位与是高14位保持原来的状态,最低两位清零,再与a按位或,则电平由a的最低两位给出
九、MIDI音乐播放程序
uc16 music1[78]={ //音乐1的数据表(奇数是音调,偶数是长度),无符号只读变量 330,750, 440,375, 494,375, 523,750, 587,375, 659,375, 587,750, 494,375, 392,375, 440,1500, 330,750, 440,375, 494,375, 523,750, 587,375, 659,375, 587,750, 494,375, 392,375, 784,1500, 659,750, 698,375, 784,375, 880,750, 784,375, 698,375, 659,750, 587,750, 659,750, 523,375, 494,375, 440,750, 440,375, 494,375, 523,750, 523,750, 494,750, 392,750, 440,3000 }; void MIDI_PLAY(void){ //MIDI音乐 u16 i,e; for(i=0;i<39;i++){ //有39个音符 for(e=0;e<music1[i*2]*music1[i*2+1]/1000;e++){ //每个音符播放时间长度 GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(0)); //蜂鸣器接口输出0 delay_us(500000/music1[i*2]); //延时 GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(1)); //蜂鸣器接口输出高电平1 delay_us(500000/music1[i*2]); //延时 } } }
音调为赫兹,长度为毫秒