STM32基础入门学习笔记:核心板 电路原理与驱动编程

这篇具有很好参考价值的文章主要介绍了STM32基础入门学习笔记:核心板 电路原理与驱动编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章目录:

一:LED灯操作 

1.LED灯的点亮和熄灭 延迟闪烁

main.c 

led.c

led.h

BitAction枚举

2.LED呼吸灯(灯的强弱交替变化)

main.c 

delay.c

3.按键控制LED灯

key.h

key.c

main.c 

二:FLASH读写程序(有记忆可以保存断电之前的状态)

flash.h

flash.c

main.c

flash操作注意事项

三:蜂鸣器驱动程序(按键有声音)

buzzer.h

buzzer.c

main.c

四:MIDI音乐播放程序(基于蜂鸣器)

buzzer.c

buzzer.h

main.c

五:USART串口通信驱动程序(串口助手 超级终端)

1.USART发送程序(3种方法)

usart.h

usart.c

main.c

2.USART接收程序(2种方法)

2.1 查询方式接收数据 

main.c

2.2 中断方式接收数据

usart.c

3.USART串口控制程序(双向交互):通过串口助手控制LED灯的开关状态和蜂鸣器、在单片机中按键操作在串口助手中进行显示

main.c

4.超级终端串口远程控制LED程序(命令行操作形式)

usart.h

usart.c        

mian.c

六:RTC实时时钟原理驱动程序

1.基于RTC的LED走时驱动程序(BKP备用寄存器) 

rtc.h

rtc.c

main.c

2.RTC超级终端串口显示日历程序(命令行操作形式)

usart.c

main.c

七:RCC时钟复位和设置程序

rtc.c

mian.c

led.c


STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

 STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

区别:秒s、毫秒ms、微秒μs、纳秒ns、皮秒ps、飞秒fs每两级之间的换算以及之间的关系_ps和fs区别

区别:千赫kHz、兆赫MHz、吉赫GHz、太赫THz、拍赫PHz、艾赫EHz每两级之间的换算以及之间的关系_太赫兹与ghz

一:LED灯操作 

1.LED灯的点亮和熄灭 延迟闪烁

原理:输出高低电平控制

main.c 

#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "led.h"


int main (void){//主程序
	RCC_Configuration(); //时钟设置
	LED_Init();
	while(1){
		
		//方法1:采用枚举(BitAction)(1)    GPIO_WriteBit设置或者清除指定的数据端口位(端口组、端口名、位控制)
		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED1接口输出高电平1
		delay_ms(500); //延时0.5秒
		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //LED1接口输出低电平0
		delay_ms(500); //延时0.5秒
		
    	//方法2:取IO口当前状态            GPIO_ReadOutputDataBit —— 读取端口输出值
		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1-GPIO_ReadOutputDataBit(LEDPORT,LED1))); //取反LED1
		delay_ms(1000); //延时1秒

   	    //方法3:直接设置IO口              GPIO_SetBits设置指定的数据端口位(端口组、端口名)
		GPIO_SetBits(LEDPORT,LED1); //LED灯都为高电平(1)
		delay_s(1); //延时1秒
		GPIO_ResetBits(LEDPORT,LED1); //LED灯都为低电平(0)
		delay_s(1); //延时1秒

		//方法4:对_ IO 组_ 的数值写入     GPIO_Write向指定GPIO数据端口整组写入数据(端口清零置1)
		GPIO_Write(LEDPORT,0x0001); //直接数值操作将变量值写入LED
		delay_s(2); //延时1秒
		GPIO_Write(LEDPORT,0x0000); //直接数值操作将变量值写入LED
		delay_s(2); //延时1秒

        //方法5:直接控制端口。PB(1)=1;PB1端口直接赋值
	}
}

led.c

#include "led.h"       //导入自己编写的 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 a/b/c       

   GPIO_InitStructure.GPIO_Pin = LED1 | LED2; 					//选择端口号(0~15或all)              可以使用宏定义名在led.h查看引脚                        
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 			//选择IO接口工作方式                   GPIO推挽方式输出   
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 			//设置IO接口速度(2/10/50MHz)         只有io被设置为输出时才配置速度

   GPIO_Init(LEDPORT, &GPIO_InitStructure);		                //将配置的结构体内容写入到ledport-gpiob那一组端口当中	
}

led.h

#ifndef __LED_H 
#define __LED_H	 
#include "sys.h"

//#define LED1 PBout(0)// PB0   也可以直接用这   <-容易理解
//#define LED2 PBout(1)// PB1	
#define LEDPORT	GPIOB	//定义IO接口  <- 优点是移植方便 ,越到后面越需要这样编译
#define LED1	GPIO_Pin_0	//定义IO接口
#define LED2	GPIO_Pin_1	//定义IO接口

void LED_Init(void);      //LED的初始化函数声明 ,就不用再从.c程序中声明
	 				    
#endif

BitAction枚举

typedef enum
{
    Bit_REST = 0;
    Bit_SET; //默认为1这里
}BitAction;

2.LED呼吸灯(灯的强弱交替变化)

原理:调节LED灯点亮和熄灭时间的比例、呼吸灯的速度来控制 

main.c 

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"

#include "led.h"

int main (void){//主程序
	//定义需要的变量
	u8 MENU; //菜单模式		8位无符号
	u16 t,i;	//16位无符号
	RCC_Configuration(); //时钟设置

	LED_Init();//led初始化
	MENU=0;	 //初始菜单状态
	t=1;   //初始亮状态延时时长
	//主循环
	while(1){
		//菜单0	  根据菜单的值不同运行不同的程序
		if(MENU==0)
		{
			for(i=0;i<10;i++)//由于延时时间t从0~500 所以是循环变亮	   i的值可以调节呼吸灯变化的速度
			{//同一个亮度循环10次
				GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED1接口输出高电平1
				    delay_us(t);	
				GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0));//LED1接口输出高电平1
				    delay_us(501-t);	 //通过将501换成更大的值可以将亮度细分为更多的等级
			}
			t++;
			if(t==500){//由亮变暗
				MENU=1;
			}
		}
		//菜单1
		if(MENU==1)
		{
			for(i=0;i<10;i++)//由于延时时间t从500~0 所以是循环变暗
			{
				GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED1接口输出高电平1
				delay_us(t);	
				GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0));//LED1接口输出高电平1
				delay_us(501-t);	
			}
			t--;
			if(t==1){
				MENU=0;
			}
		}
	}
}

delay.c

#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;           //关闭定时器
}

3.按键控制LED灯

原理:通过读取按键端口输入进行控制

key.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"

//#define KEY1 PAin(0)// PA0
//#define KEY2 PAin(1)// PA1

#define KEYPORT	GPIOA	//定义IO接口组
#define KEY1	GPIO_Pin_0	//定义IO接口
#define KEY2	GPIO_Pin_1	//定义IO接口


void KEY_Init(void);//声明按键初始化

		 				    
#endif

key.c

#include "key.h"

void KEY_Init(void)//微动开关的接口初始化
{ 
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);       

    GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2; //选择端口号(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);//此时才把io的配置进行初始化!!!		
}

main.c 

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"

#include "key.h" 

int main (void){//主程序
	//初始化程序
	RCC_Configuration(); //时钟设置
	LED_Init();//LED初始化

	KEY_Init();//按键初始化

	//主循环
	while(1){//GPIO_ReadInputDataBit —— 读取端口输入

		//示例1:无锁存    按着亮,松开熄灭
		    if(GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
			    GPIO_ResetBits(LEDPORT,LED1); //LED灯都为低电平(0) 
		        }else{	
        	    GPIO_SetBits(LEDPORT,LED1); //LED灯都为高电平(1) 
		    }
		    if(GPIO_ReadInputDataBit(KEYPORT,KEY2)){ //读按键接口的电平
			    GPIO_ResetBits(LEDPORT,LED2); //LED灯都为低电平(0) 
		    }else{	
        	    GPIO_SetBits(LEDPORT,LED2); //LED灯都为高电平(1) 
		    }
		//示例2:无锁存
		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(!GPIO_ReadInputDataBit(KEYPORT,KEY1))); 

		//示例3:有锁存    第一次按灯亮,松开还是亮,第二次按熄灭
		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)); //等待按键松开 
			}
		}

		//示例4:有锁存    按第一次第一个灯亮,按第二次第一个灯熄灭第二个灯亮,按第三次亮灯亮,第四次按同时熄灭
		if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
			delay_ms(20); //延时20ms去抖动
			if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
				//在2个LED上显示二进制加法
				a++; //变量加1
				if(a>3){ //当变量大于3时清0
					a=0; 
				}
				GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)
                                       //以a的值赋给端口组,实现 “仅D1亮、仅D2亮、都亮、都灭” 的循环
                                       //                          0       1       2     3
				while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 
			}
		}

	}
}

二:FLASH读写程序(有记忆可以保存断电之前的状态)

原理:通过在指定页擦除和指定地址写入数据进行控制 

如何把存储数据到FLASH中?

在Basic文件夹中——>创建一个FLASH文件夹(flash.c flash.h)


注意查看lib文件夹下的flash.c是否存在

flash.h

#ifndef __FLASH_H
#define __FLASH_H 			   
#include "sys.h"

#define LEDPORT	GPIOB	//定义IO接口  <- 优点是移植方便 ,越到后面越需要这样编译
#define KEY1	GPIO_Pin_0	//定义IO接口
#define KEY2	GPIO_Pin_1	//定义IO接口

void LED_Init(void);      //LED的初始化函数声明 ,就不用再从.c程序中声明
void KEY_Init(void);      //声明按键初始化


void FLASH_W(u32 add,u16 dat);
//void FLASH_W(u32 add,u16 dat,u16 dat2);多个情况;若果过多可以使用数组或指针来解决
u16 FLASH_R(u32 add);

#endif

flash.c

#include "flash.h"//导入头文件

//FLASH写入数据
void FLASH_W(u32 add,u16 dat){ //参数1:32位FLASH地址。参数2:16位数据
//void FLASH_W(u32 add,u16 dat,u16 dat2){//多个情况
    //	 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_ProgramHalfWord(add+1,dat2);//多个情况(是加上这条)

     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;
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h" 

#include "flash.h" //导入Flash的头文件

#define FLASH_START_ADDR  0x0801f000	  //写入的起始地址

int main (void){//主程序
	u16 a; //定义变量
	//初始化程序
	RCC_Configuration(); //时钟设置
	LED_Init();//LED初始化
	KEY_Init();//按键初始化

    a = FLASH_R(FLASH_START_ADDR);//从指定页的地址读FLASH
        //b = FLASH_R(FLASH_START_ADDR);多个情况
	GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)
                           //整组写入LED(PB组)
        //GPIO_Write(LEDPORT,a,b);多个情况


	//主循环
	while(1){

		//示例4:有锁存
		if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
			delay_ms(20); //延时20ms去抖动
			if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
				//在2个LED上显示二进制加法
				a++; //变量加1
				if(a>3){ //当变量大于3时清0
					a=0; 
				}
				GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)

				FLASH_W(FLASH_START_ADDR,a); //将led的状态值a写入,指定FLASH起始页的地址内
                //FLASH_W(FLASH_START_ADDR,a,b);多个情况

				while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 
			}
		}
	}
}

flash操作注意事项

操作一定要先擦后写

每页是1024个地址,起始地址Ox08000000

擦除操作以页为单位,写操作则必须以16位宽度为单位,允许跨页写入

STM32内置FLASH擦或写时,必须打开外部/内部高速振荡器

FLASH可多次擦写10万次,不可死循环擦写

擦写时要避开用户程序存储区的区域,否则会擦掉用户程序导致错误

擦除一页要10ms(对于1k大小的一页),比较慢。而且不能单个字节的擦写

擦除只能以页擦除:临时存储写入地址(空白区域尽量靠后)   不能和    下载用户程序(第0页开始)相互冲突 

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

三:蜂鸣器驱动程序(按键有声音)

原理:通过延迟时间决定频率震动 和 高低电平循环次数决定持续时长 进行控制 

核心板蜂鸣器电路

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

在Hardware文件夹中——>创建一个BUZZER文件夹(buzzer.c buzzer.h)

buzzer.h

#ifndef    ——BUZZZER_H
#define    ——BUZZZER_H
#include "sys.h"


#define BUZZERPORT	GPIOB	 //定义IO接口     端口组PB5
#define LED1 GPIO_ Pin_o //定义工o接口
#define LED2 GPIO_ Pin_1 //定义工o接口


#define BUZZER	GPIO_Pin_5	 //定义IO接口     IO端口


void BUZZER_Init(void);   //初始化
void BUZZER_BEEP1(void);  //响一声


#endif

buzzer.c

#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	,断开线路,保护蜂鸣器不被损坏或烧坏
    }



//蜂鸣器响一声t
//延迟时间长度决定总周期的长度(修改声音的频率音调)    循环次数i决定蜂鸣器发出(声音持续时间的长度)
void BUZZER_BEEP1(void)                                  //1kHz
    {                                                    //蜂鸣器响一声
    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);                               //延时		
        }
    }

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "flash.h" 

#include "buzzer.h" 
#define FLASH_START_ADDR  0x0801f000	  //写入的起始地址

int main (void){//主程序
	u8 a; //定义变量

	//初始化程序
	RCC_Configuration(); //时钟设置
	LED_Init();//LED初始化
	KEY_Init();//按键初始化

	BUZZER_Init();//蜂鸣器初始化
	BUZZER_BEEP1();//蜂鸣器响1khz

	a = FLASH_R(FLASH_START_ADDR);//从指定页的地址读FLASH

    //处理PB1 PB2
	//因为led是PB0和PB1蜂鸣器的接口,同时是PB5也是。一设置低电平PB端口就全部为0了(默认是PB0 PB1,但这里加入了PB5 就会使蜂鸣器长时间导致发热)
	    //GPIO_ReadOutputData读取整组(LED在GPIOB组)的io口的电平状态	 按位与优先级高
	    //0xfffc&GPIO_ReadOutputData(LEDPORT))高14位保持io口的原来状态,低两位清零
	    //然后与a进行与运算操作变量a的最低两位,再写入PB组的最低两位,其他PB组的其他位不变:将led状态写入,也就是说led在状态写入的时候不会影响其他io电平
    GPIO_Write(LEDPORT,a|0xfffc&GPIO_ReadOutputData(LEDPORT)); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)

	//主循环
	while(1){
		//示例4:有锁存
		if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
			delay_ms(20); //延时20ms去抖动
			if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
				//在2个LED上显示二进制加法
				a++; //变量加1
				if(a>3){ //当变量大于3时清0
					a=0; 
				}
				GPIO_Write(LEDPORT,a|0xfffc&GPIO_ReadOutputData(LEDPORT)); //整组写入不是某个引脚直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)
				//a=1是001->pa0=1,a=2是010->pa0=0 pa1=1,a=3是011->pa0=1 pa1=1,a=4是100->pa0=0 pa1=0
				BUZZER_BEEP1();//蜂鸣器音1

				FLASH_W(FLASH_START_ADDR,a); //从指定页的地址写入FLASH

				while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 
                //BUZZER_BEEP2();//蜂鸣器音2
			}
		}
	}
}

四:MIDI音乐播放程序(基于蜂鸣器)

原理:通过改变音调频率 和 时间长度使用蜂鸣器进行控制 

音调与频率的关系 

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

buzzer.c

uc16 music1[78]={ //音乐的数据表(奇数是音调频率,偶数是时间长度),存于flash中
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
};



//MIDI音乐
void MIDI_PLAY(void){     
	u16 i,e;
    39个音符循环一次
	for(i=0;i<39;i++){    
        //循环不同次数达到让同一个音符播放一段时间长度
        //music1[i*2]——>0——>330    music1[i*2+1]——>1——>750    (1赫兹=1次/秒) 
        //意思:每秒钟震动330次[Hz],750毫秒[T]震动多少次呢[次数=Hz*T]?
		for(e=0;e<music1[i*2]*music1[i*2+1]/1000;e++){    //转化为毫秒us 也就是xxx/1000
            //方波的时间,音效音调高低频率,高电平的延时时间500000us+低电平的延时时间500000us=形成1秒频率周期	
			GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(0)); //蜂鸣器接口输出0
			    delay_us(500000/music1[i*2]); //延时    1/Hz=s
			GPIO_WriteBit(BUZZERPORT,BUZZER,(BitAction)(1)); //蜂鸣器接口输出高电平1
			    delay_us(500000/music1[i*2]); //延时	
		}	
	}
}

buzzer.h

void MIDI PLAY(void);    //MIDI_PLAY函数声明

main.c

BUZZER_Init ();//蜂鸣器初始化
//BUZZER_BEEP1(0)://蜂鸣器响1khz

MIDI_PLAY(); //播放MIDI音乐

五:USART串口通信驱动程序(串口助手 超级终端)

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

 STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

DYS串口助手使用:设置自己对应的端口号(自定义)”C0M4“、波特率(自定义)”115200“、发送模式(自定义)”数值“、接收模式(自定义)”数值“
                 最后点击打开端口(自定义)、之后关闭端口(自定义)

                 ”数值“:16进制    ”字符“:ASCLL码    
                 发送和接收数据可以相互转换:ASCLL码与进制对应表

在Lib文件交接加入:stm32f10x_usart.c


串口助手和FlyMcu:如果公用一个端口,那么就不能同时使用

新建文件夹

Basic——>usart——>usart.cusart.h

1.USART发送程序(3种方法)

usart.h

#ifndef __USART_H
#define __USART_H
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "stdio.h"	
#include "sys.h" 

#define USART_n		USART1  //定义使用printf函数的串口,其他串口要使用USART_printf专用函数发送

#define USART1_REC_LEN  			200  	//定义USART1最大接收字节数
#define USART2_REC_LEN  			200  	//定义USART2最大接收字节数
#define USART3_REC_LEN  			200  	//定义USART3最大接收字节数

//不使用某个串口时要禁止此串口,以减少编译量
#define EN_USART1 			1		//使能(1)/禁止(0)串口1
#define EN_USART2 			0		//使能(1)/禁止(0)串口2
#define EN_USART3 			0		//使能(1)/禁止(0)串口3
	  	
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u8  USART2_RX_BUF[USART2_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8  USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
 
extern u16 USART1_RX_STA;         		//接收状态标记	
extern u16 USART2_RX_STA;         		//接收状态标记	
extern u16 USART3_RX_STA;         		//接收状态标记	

//函数声明
void USART1_Init(u32 bound);//串口1初始化并启动
void USART2_Init(u32 bound);//串口2初始化并启动
void USART3_Init(u32 bound);//串口3初始化并启动
void USART1_printf(char* fmt,...); //串口1的专用printf函数
void USART2_printf(char* fmt,...); //串口2的专用printf函数
void USART3_printf(char* fmt,...); //串口3的专用printf函数

#endif

usart.c

#include "sys.h"
#include "usart.h"
	  	 
//使UASRT串口可用printf函数发送
//在usart.h文件里可更换使用printf函数的串口号	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE {
	int handle; 
}; 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x){ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f){      
	while((USART_n->SR&0X40)==0);//循环发送,直到发送完毕   
    USART_n->DR = (u8) ch;      
	return ch;
}
#endif 





/*USART1串口相关程序*/
#if EN_USART1   //USART1使用与屏蔽选择
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
                                      //接收状态
                                      //bit15,	接收完成标志
                                      //bit14,	接收到0x0d
                                      //bit13~0,	接收到的有效字节数目
u16 USART1_RX_STA=0;                  //接收状态标记	  

/*USART1专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART1_printf("123"); //向USART2发送字符123*/
void USART1_printf (char *fmt, ...)
{ 
  char buffer[USART1_REC_LEN+1];  // 数据长度
  u8 i = 0;	
  va_list arg_ptr;
  va_start(arg_ptr, fmt);  
  vsnprintf(buffer, USART1_REC_LEN+1, fmt, arg_ptr);
  while ((i < USART1_REC_LEN) && (i < strlen(buffer)))
  {
    USART_SendData(USART1, (u8) buffer[i++]);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); 
  }
  va_end(arg_ptr);
}




//串口1初始化并启动
void USART1_Init(u32 bound){ 
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;	 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

  //USART1_TX   PA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);  

  //USART1_RX	  PA.10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure); 

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;   //抢占优先级3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
  NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 

  //USART 初始化设置
  USART_InitStructure.USART_BaudRate = bound;//波特率 一般设置为9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  USART_Init(USART1, &USART_InitStructure); //初始化串口
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启ENABLE/关闭DISABLE中断
  USART_Cmd(USART1, ENABLE);                    //使能串口 
}


//串口1的中断处理程序
void USART1_IRQHandler(void)
{                       //串口1中断服务程序(固定的函数名不能修改)	
  u8 Res;
                        //以下是字符串接收到USART1_RX_BUF[]的程序,(USART1_RX_STA&0x3FFF)是数据的长度(不包括回车)
                        //当(USART1_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。
                        //在主函数里写判断if(USART1_RX_STA&0xC000),然后读USART1_RX_BUF[]数组,读到0x0d 0x0a即是结束。
                        //注意在主函数处理完串口数据后,要将USART1_RX_STA清0
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {                                     //接收中断(接收到的数据必须是0x0d 0x0a结尾)		
    Res =USART_ReceiveData(USART1);     //(USART1->DR);	//读取接收到的数据
    printf("%c",Res);                   //把收到的数据以 a符号变量 发送回电脑		
    if((USART1_RX_STA&0x8000)==0)
    {                                   //接收未完成			
      if(USART1_RX_STA&0x4000)
      {                                 //接收到了0x0d				
        if(Res!=0x0a)
        {
          USART1_RX_STA=0;              //接收错误,重新开始
        }
        else
        {
          USART1_RX_STA|=0x8000;	    //接收完成了 
        }
      }
      else
      {                                 //还没收到0X0D					
        if(Res==0x0d)USART1_RX_STA|=0x4000;
        else
        {
          USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组
          USART1_RX_STA++;	            //数据长度计数加1
          if(USART1_RX_STA>(USART1_REC_LEN-1))
          {
            USART1_RX_STA=0;            //接收数据错误,重新开始接收	  
          }
        }		 
      }
    }   		 
  } 
} 
#endif	

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "usart.h"



int main (void)
{                        //主程序
  u8 a=7,b=8;
                         //初始化程序
  RCC_Configuration();   //时钟设置
  USART1_Init(115200);   //串口初始化(参数是波特率)发送和接收波特率必须完全相同

  //主循环
  while(1)
  {
    /* 发送方法1    单个 */
    USART_SendData(USART1, 0x55);    //发送单个数值(那个端口,发送什么数据)
    //USART_SendData(USART1, 'U');  
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); //检查发送中断标志位

    /* 发送方法2     多个*/
    //	printf("STM32F103 ");          //纯字符串发送数据到串口
    //	printf("STM32 %d %d ",a,b);    //纯字符串和变量发送数据到串口,a符号变量
        
    /* 发送方法3     多个    专用的*/
    //	USART1_printf("STM32 %d %d ",a,b);

    delay_ms(1000);                    //延时
  }
}

2.USART接收程序(2种方法)

2.1 查询方式接收数据 

查询要在usart.c的USART初始化设置中关闭中断(当串口接收到数据时才不会跳到中断函数中)ENABLE——>DISABLE

main.c
u8 a;

while(){
    //查询方式接收(发回去,又接收回来),失去实时性
    if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)!= RESET){    //查询串口待处理标志位    接收数据寄存器非空标志位
        a =USART_ReceiveData(USART1);                           //读取接收到的数据
        printf ("%c",a);                                        //把收到的数据发送回电脑
    }
}

2.2 中断方式接收数据

 中断要在usart.c的USART初始化设置中关闭中断(当串口接收到数据时才不会跳到中断函数中)DISABLE——>ENABLE

usart.c
//中断方式接收程序解读
void USART1_IRQHandler(void){ //串口1中断服务程序(固定的函数名不能修改)	
	u8 a;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){      //接收中断(接收到的数据必须是0x0d 0x0a结尾)		
		a=USART_ReceiveData(USART1);                            //(USART1->DR);	//读取接收到的数据
		printf("%c",a);                                         //把收到的数据以 a符号变量 发送回电脑	
		}

3.USART串口控制程序(双向交互):通过串口助手控制LED灯的开关状态和蜂鸣器、在单片机中按键操作在串口助手中进行显示

这里需要使用超级终端:并且可以改变终端输出的颜色背景 

输入输出可以一起显示


建立连接
    1.文件——>新建连接——>选择合适的串口(自定义)COM4——>确定
    2.修改
        串口(自定义):COM4
        波特率(自定义):115200
        编码:GB2312
    3.最后点击确定


断开连接
    串口名字上——>右键——>关闭

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"
int main (void){//主程序
	u8 a;
	//初始化程序
	RCC_Configuration(); //时钟设置
	LED_Init();//LED初始化
	KEY_Init();//按键初始化
	BUZZER_Init();//蜂鸣器初始化
	USART1_Init(115200); //串口初始化(参数是波特率)

	//主循环
	while(1){

		//采用查询方式接收   
        //查询要在usart.c的USART初始化设置中关闭中断(当串口接收到数据时才不会跳到中断函数中)ENABLE——>DISABLE 
		if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET){  //查询串口待处理标志位
			a =USART_ReceiveData(USART1);//读取接收到的数据
			switch (a){
				case '0':
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //LED控制
					printf("%c:LED1 OFF ",a); //不能使用中文
					break;
				case '1':
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED控制
					printf("%c:LED1 ON ",a); 
					break;
				case '2':
					BUZZER_BEEP1(); //蜂鸣一声
					printf("%c:BUZZER ",a); //把收到的数据发送回电脑
					break;
				default:
					break;
			}		  
		}

		//按键控制
		if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
			delay_ms(20); //延时20ms去抖动
			if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
				while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 
				printf("KEY1 "); 
                printf("KEY1 "); 
			}
		}		 
		if(!GPIO_ReadInputDataBit(KEYPORT,KEY2)){ //读按键接口的电平
			delay_ms(20); //延时20ms去抖动
			if(!GPIO_ReadInputDataBit(KEYPORT,KEY2)){ //读按键接口的电平
				while(!GPIO_ReadInputDataBit(KEYPORT,KEY2)); //等待按键松开
 
				printf("\O33[1;40;32m KEY2 ");            //改变字体颜色和背景色
                printf("\O33[1;40;32m KEY2 \O33[om");     //改变字体颜色和背景色,之后又改回来
                printf("\O33[1;40;32m KEY2 /n/r");        //改变字体颜色和背景色,再次按显示到下一行
			}
		}		 

//      delay_ms(1000); //延时
	}
}

4.超级终端串口远程控制LED程序(命令行操作形式)

usart.h

#define USART1_REC_LEN    200;    //定义USART1最大接收字节数
#define USART2_REC_LEN    200;
#define USART3_REC_LEN    200;


//全局变量声明
extern u8 USART1_RX_BUF[USART1_REC_LEN];    //接收缓存,最大USART_REC_LEN个字节末字节为换行符
extern u8 USART2_RX_BUF[USART2_REC_LEN];    
extern u8 USART3_RX_BUF[USART3_REC_LEN];   


extern u16 USART1_RX_STA;    //接收状态标记
extern u16 USART2_RX_STA;
extern u16 USART3_RX_STA;

usart.c        

u8 USART1_RX_BUF[USART1_REC_LEN];    //接收缓冲,最大USART1_REC_LEN个字节
//全局变量
u16 USART1_RX_STA=0;    //接收状态标记



//通过中断函数接收数据
//中断要在usart.c的USART初始化设置中关闭中断(当串口接收到数据时才不会跳到中断函数中)DISABLE——>ENABLE
void USART1_IRQHandler(void)
{ //串口1中断服务程序(固定的函数名不能修改)	
  u8 Res;
  //以下是字符串接收到USART_RX_BUF[]的程序,(USART_RX_STA&0x3FFF)是数据的长度(不包括回车)
  //当(USART_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。
  //在主函数里写判断if(USART_RX_STA&0xC000),然后读USART_RX_BUF[]数组,读到0x0d 0x0a即是结束
  //注意在主函数处理完串口数据后,要将USART_RX_STA清0

//多字符指令接收
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {    //接收中断(接收到的数据必须是0x0d回车符 0x0a结尾)		
    Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
    printf("%c",Res); //把收到的数据以 a符号变量 发送回电脑
	
    //USART1_RX_STA初始值为0	
    if((USART1_RX_STA&0x8000)==0) {    //接收未完成	接收状态标记	最高位第1位	    定义了初始为0
      if(USART1_RX_STA&0x4000){        //接收到了0x0d		        最高位第2位
        //判断是不是0x0a /r			
        if(Res!=0x0a)
            {USART1_RX_STA=0;}             //接收错误,重新开始,状态位清零
        else
            {USART1_RX_STA|=0x8000;}       //接收完成了,是0x0a,那么把最高位状态位置1【11】
      }else{//还没收到0X0D     /n按键	    回车键=ox0D+0x0a组合=/n/r	
        //是回车,第二位置1			
        if(Res==0x0d){USART1_RX_STA|=0x4000;}    //0100 0 0 0     最高位第2位 置1【11】
        //不是回车去除掉最高两位
        else
        {
          USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组    最高位的2位去除掉	    0011FFF

          USART1_RX_STA++;	//数据长度计数加1
          if(USART1_RX_STA>(USART1_REC_LEN-1))     //读到的值超过数组的最大值
              {USART1_RX_STA=0;}                   //接收数据错误,重新开始接收,状态标志清零	            
        }		 
      }
    }
  }
}

mian.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"


int main (void){//主程序
	RCC_Configuration();
	LED_Init();//LED初始化
	KEY_Init();//按键初始化
	BUZZER_Init();//蜂鸣器初始化
	USART1_Init(115200); //串口初始化,参数中写波特率

	USART1_RX_STA=0xC000; //初始值设为有回车的状态,即显示一次欢迎词

	while(1){
        //判断最高位和最高位的第二位 是不是1    1100 0 0 0
		if(USART1_RX_STA&0xC000){ //如果标志位是0xC000表示收到数据串完成,可以处理。
            //判断数据是0个,从而看是不是按了回车    去掉状态变量最高两位0011FFF(表示没有输入任何字符)
			if((USART1_RX_STA&0x3FFF)==0){ //单独的回车键再显示一次欢迎词
				printf("\033[1;47;33m\r\n"); //设置颜色(参考超级终端使用)
				printf(" 1y--开LED1灯      1n--关LED1灯 \r\n");
				printf(" 2y--开LED2灯      2n--关LED2灯 \r\n");
				printf(" 请输入控制指令,按回车键执行! \033[0m\r\n");
			}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='1' && USART1_RX_BUF[1]=='y'){ //判断数据是不是2个,第一个数据是不是“1”,第二个是不是“y”
				GPIO_SetBits(LEDPORT,LED1); //LED灯都为高电平(1)
				printf("1y -- LED1灯已经点亮!\r\n");
			}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='1' && USART1_RX_BUF[1]=='n'){
				GPIO_ResetBits(LEDPORT,LED1); LED灯都为低电平(0)
				printf("1n -- LED1灯已经熄灭!\r\n");
			}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='2' && USART1_RX_BUF[1]=='y'){
				GPIO_SetBits(LEDPORT,LED2); //LED灯都为高电平(1)
				printf("2y -- LED2灯已经点亮!\r\n");
			}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='2' && USART1_RX_BUF[1]=='n'){
				GPIO_ResetBits(LEDPORT,LED2); LED灯都为低电平(0)
				printf("2n -- LED2灯已经熄灭!\r\n");
			}else{ //如果以上都不是,即是错误的指令。
				printf("指令错误!\r\n"); 
			}
			USART1_RX_STA=0; //将串口数据标志位清0
		}
	}
}

六:RTC实时时钟原理驱动程序

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

 STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

1.基于RTC的LED走时驱动程序(BKP备用寄存器) 

新建文件夹

Basic——>rtc文件夹——>rtc.c rtc.h


在Lib文件夹下 添加:stm32f10x_rtc.c

LED1代表秒:奇数点亮,偶数熄灭;LED2代表分钟:奇数点亮,偶数熄灭

rtc.h

#ifndef __RTC_H
#define __RTC_H	 
#include "sys.h" 


//全局变量的声明,在rtc.c文件中定义
//以下2条是使用extern语句声明全局变量,时钟计算出的年月日时分秒
//注意:这里不能给变量赋值

extern u16 ryear;
extern u8 rmon,rday,rhour,rmin,rsec,rweek;    //年月日时分秒


//函数声明
u8 RTC_Get(void);                                               //读出当前时间值
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);    //写入当前时间值

void RTC_First_Config(void);                                    //首次启用RTC的设置(全部初始化)
void RTC_Config(void);                                          //实时时钟初始化(部分初始化)

//下面包含在时间读写函数里面,不需要单独调用
u8 Is_Leap_Year(u16 year);                                      //判断是否是闰年函数                    
u8 RTC_Get_Week(u16 year,u8 month,u8 day);                      //按年月日计算星期几

#endif

rtc.c

#include "sys.h"
#include "rtc.h"


//以下2条全局变量--用于RTC时间的读取
u16 ryear; //4位年
u8 rmon,rday,rhour,rmin,rsec,rweek;//2位月日时分秒周


//首次启用RTC的初始化设置
void RTC_First_Config(void){ 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);//启用PWR电源管理部分和BKP备用寄存器的时钟(from APB1)
    PWR_BackupAccessCmd(ENABLE);//后备域解锁    使能或者失能RTC和后备寄存器访问
    BKP_DeInit();//备份寄存器模块复位            将外设BKP的全部寄存器重设为缺省值
    RCC_LSEConfig(RCC_LSE_ON);//外部32.768KHZ晶振开启   
    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待稳定    
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//RTC时钟源配置成LSE(外部低速晶振32.768KHZ)    
    RCC_RTCCLKCmd(ENABLE);//RTC开启    
    RTC_WaitForSynchro();//开启后需要等待APB1时钟与RTC时钟同步,才能读写寄存器    
    RTC_WaitForLastTask();//读写寄存器前,要确定上一个操作已经结束
    RTC_SetPrescaler(32767);//设置RTC分频器,使RTC时钟为1Hz,RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)   
    RTC_WaitForLastTask();//等待寄存器写入完成	
    //当不使用RTC秒中断,可以屏蔽下面2条
//    RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断   
//    RTC_WaitForLastTask();//等待写入完成
}


//实时时钟一般初始化设置
void RTC_Config(void){ 
    //在BKP的后备寄存器1中,存了一个特殊字符0xA5A5
    //第一次上电或后备电源掉电后,该寄存器数据丢失,表明RTC数据丢失,需要重新配置
    if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//判断寄存数据是否丢失  从指定的后备寄存器中读取数据       
        RTC_First_Config();//重新配置RTC        
        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
    }else{
		//若后备寄存器没有掉电,则无需重新配置RTC
        //这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型
        if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET){
            //这是上电复位
        }
        else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){
            //这是外部RST管脚复位
        }       
        RCC_ClearFlag();//清除RCC中复位标志

        //虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行
        //但是每次上电后,还是要使能RTCCLK
        RCC_RTCCLKCmd(ENABLE);//使能RTCCLK        
        RTC_WaitForSynchro();//等待RTC时钟与APB1时钟同步

        //当不使用RTC秒中断,可以屏蔽下面2条
//        RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断        
//        RTC_WaitForLastTask();//等待操作完成
    }
    //是否启用rtc的输出功能,一般情况下不使用
	#ifdef RTCClockOutput_Enable   
	    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
	    PWR_BackupAccessCmd(ENABLE);   
	    BKP_TamperPinCmd(DISABLE);   
	    BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
	#endif
}



//RTC时钟1秒触发中断函数(名称固定不可修改)
void RTC_IRQHandler(void){
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET){
        //写入自己的程序
	}
	RTC_ClearITPendingBit(RTC_IT_SEC); 
	RTC_WaitForLastTask();
}

//闹钟中断处理(启用时必须调高其优先级)
void RTCAlarm_IRQHandler(void){
	if(RTC_GetITStatus(RTC_IT_ALR) != RESET){
	
	}
	RTC_ClearITPendingBit(RTC_IT_ALR);
	RTC_WaitForLastTask();
}



//时间读写计算
//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year){                    
	if(year%4==0){ //必须能被4整除
		if(year%100==0){		
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除          
			else return 0;  
		}else return 1;  
	}else return 0;
}                           
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份

//月份数据表                                                                       
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表  
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表

//写入时间
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec){ //写入当前时间(1970~2099年有效),
	u16 t;
	u32 seccount=0;
	if(syear<2000||syear>2099)return 1;//syear范围1970-2099,此处设置范围为2000-2099       
	for(t=1970;t<syear;t++){ //把所有年份的秒钟相加
		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
		else seccount+=31536000;                    //平年的秒钟数
	}
	smon-=1;
	for(t=0;t<smon;t++){         //把前面月份的秒钟数相加
		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数        
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
	seccount+=(u32)hour*3600;//小时秒钟数
	seccount+=(u32)min*60;      //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去
	RTC_First_Config(); //重新初始化时钟
	BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
	RTC_SetCounter(seccount);//把换算好的计数器值写入
	RTC_WaitForLastTask(); //等待写入完成
	return 0; //返回值:0,成功;其他:错误代码.    
}

//读出时间
u8 RTC_Get(void){//读出当前时间值 //返回值:0,成功;其他:错误代码.
	static u16 daycnt=0;
	u32 timecount=0;
	u32 temp=0;
	u16 temp1=0;
	timecount=RTC_GetCounter();		
	temp=timecount/86400;   //得到天数(秒钟数对应的)
	if(daycnt!=temp){//超过一天了
		daycnt=temp;
		temp1=1970;  //从1970年开始
		while(temp>=365){
		     if(Is_Leap_Year(temp1)){//是闰年
			     if(temp>=366)temp-=366;//闰年的秒钟数
			     else {temp1++;break;} 
		     }
		     else temp-=365;       //平年
		     temp1++; 
		}  
		ryear=temp1;//得到年份
		temp1=0;
		while(temp>=28){//超过了一个月
			if(Is_Leap_Year(ryear)&&temp1==1){//当年是不是闰年/2月份
				if(temp>=29)temp-=29;//闰年的秒钟数
				else break;
			}else{
	            if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
	            else break;
			}
			temp1++; 
		}
		rmon=temp1+1;//得到月份
		rday=temp+1;  //得到日期
	}
	temp=timecount%86400;     //得到秒钟数      
	rhour=temp/3600;     //小时
	rmin=(temp%3600)/60; //分钟     
	rsec=(temp%3600)%60; //秒钟
	rweek=RTC_Get_Week(ryear,rmon,rday);//获取星期  
	return 0;
}    


//获取星期几
//按年月日计算星期(只允许1901-2099年)//已由RTC_Get调用    
u8 RTC_Get_Week(u16 year,u8 month,u8 day){ 
	u16 temp2;
	u8 yearH,yearL;
	yearH=year/100;     
	yearL=year%100;
	// 如果为21世纪,年份数加100 
	if (yearH>19)yearL+=100;
	// 所过闰年数只算1900年之后的 
	temp2=yearL+yearL/4;
	temp2=temp2%7;
	temp2=temp2+day+table_week[month-1];
	if (yearL%4==0&&month<3)temp2--;
	return(temp2%7); //返回星期值
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"

#include "rtc.h"


int main (void){//主程序
	RCC_Configuration(); //系统时钟初始化

	RTC_Config(); //实时时钟初始化
 
	LED_Init();//LED初始化
	KEY_Init();//按键初始化
	BUZZER_Init();//蜂鸣器初始化
	USART1_Init(115200); //串口初始化,参数中写波特率
	USART1_RX_STA=0xC000; //初始值设为有回车的状态,即显示一次欢迎词
	while(1){
		if(RTC_Get()==0){ //读出时间值,同时判断返回值是不是0,非0时读取的值是错误的。	
			GPIO_WriteBit(LEDPORT,LED1,(BitAction)(rsec%2)); //LED1接口    读出秒数,取余表示led的状态    
			GPIO_WriteBit(LEDPORT,LED2,(BitAction)(rmin%2)); //LED2接口    读出分钟数,取余表示led的状态    
		}
	}
}

2.RTC超级终端串口显示日历程序(命令行操作形式)

在超级终端按回车键,之后显示更新时间 初始化时钟 输入设置时间

usart.c

//开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启ENABLE/关闭DISABLE中断

main.c

#include "stm32f10x.h"    //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"
#include "rtc.h"




int main (void){//主程序
  u8 bya;
  RCC_Configuration(); //系统时钟初始化
  RTC_Config(); //实时时钟初始化
  LED_Init();//LED初始化
  KEY_Init();//按键初始化
  BUZZER_Init();//蜂鸣器初始化
  USART1_Init(115200); //串口初始化,参数中写波特率
  USART1_RX_STA=0xC000; //初始值设为有回车的状态,即显示一次欢迎词


  while(1)
  {
    if(USART1_RX_STA&0xC000)
    { //如果标志位是0xC000表示收到数据串完成,可以处理。
      if((USART1_RX_STA&0x3FFF)==0)
      { //单独的回车键再显示一次欢迎词
        if(RTC_Get()==0)
        { //读出时间值,同时判断返回值是不是0,非0时读取的值是错误的。
          printf(" 洋桃开发板STM32实时时钟测试程序   \r\n");
          printf(" 现在实时时间:%d-%d-%d %d:%d:%d  ",ryear,rmon,rday,rhour,rmin,rsec);//显示日期时间
          //分钟和秒钟的各位与十位分开:单独显示  
          printf(" 现在实时时间:%d-%d-%d %d:%d%d:%d%d  ",ryear,rmon,rday,rhour,rmin/10,rmin%10,rsec/10,rsec%10);//显示日期时间          

          if(rweek==0)printf("星期日   \r\n");//rweek值为0时表示星期日
          if(rweek==1)printf("星期一   \r\n");
          if(rweek==2)printf("星期二   \r\n");
          if(rweek==3)printf("星期三   \r\n");
          if(rweek==4)printf("星期四   \r\n");
          if(rweek==5)printf("星期五   \r\n");
          if(rweek==6)printf("星期六   \r\n");
          printf(" 单按回车键更新时间。输入字母C初始化时钟 \r\n");
          printf(" 请输入设置时间,格式20170806120000,按回车键确定! \r\n");
        }
        else
        {
          printf("读取失败!\r\n");
        }
      }
      else if((USART1_RX_STA&0x3FFF)==1)
      { //判断数据是不是2个
        if(USART1_RX_BUF[0]=='c' || USART1_RX_BUF[0]=='C')
        {
          RTC_First_Config(); //键盘输入c或C,初始化时钟
          BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
          printf("初始化成功!      \r\n");//显示初始化成功
        }
        else
        {
          printf("指令错误!          \r\n"); //显示指令错误!
        } 
      }
      else if((USART1_RX_STA&0x3FFF)==14)
      { //判断数据是不是14个
        //将超级终端发过来的数据换算并写入RTC    ox30是偏移量=0(实现1~9的对应)    
        ryear = (USART1_RX_BUF[0]-0x30)*1000+(USART1_RX_BUF[1]-0x30)*100+(USART1_RX_BUF[2]-0x30)*10+USART1_RX_BUF[3]-0x30;
        rmon = (USART1_RX_BUF[4]-0x30)*10+USART1_RX_BUF[5]-0x30;//串口发来的是字符,减0x30后才能得到十进制0~9的数据
        rday = (USART1_RX_BUF[6]-0x30)*10+USART1_RX_BUF[7]-0x30;
        rhour = (USART1_RX_BUF[8]-0x30)*10+USART1_RX_BUF[9]-0x30;
        rmin = (USART1_RX_BUF[10]-0x30)*10+USART1_RX_BUF[11]-0x30;
        rsec = (USART1_RX_BUF[12]-0x30)*10+USART1_RX_BUF[13]-0x30;
        bya=RTC_Set(ryear,rmon,rday,rhour,rmin,rsec); //将数据写入RTC计算器的程序
        if(bya==0)printf("写入成功!      \r\n");//显示写入成功 
        else printf("写入失败!       \r\n"); //显示写入失败
      }
      else
      { //如果以上都不是,即是错误的指令。
        printf("指令错误!          \r\n"); //如果不是以上正确的操作,显示指令错误!
      }
      USART1_RX_STA=0; //将串口数据标志位清0
    }
  }
}

七:RCC时钟复位和设置程序

STM32基础入门学习笔记:核心板 电路原理与驱动编程,# STM32,学习,笔记

左边——产生阻频;左边——分配阻频文章来源地址https://www.toymoban.com/news/detail-636003.html

rtc.c

//RCC时钟的设置  
void RCC_Configuration(void)
  { 
    ErrorStatus HSEStartUpStatus;       //枚举定义
    RCC_DeInit();              /* RCC system reset(for debug purpose) RCC寄存器恢复初始化值*/   
    RCC_HSEConfig(RCC_HSE_ON); /* Enable HSE 使能外部高速晶振*/   
    HSEStartUpStatus = RCC_WaitForHSEStartUp(); /* Wait till HSE is ready 等待外部高速晶振使能完成*/   
    if(HSEStartUpStatus == SUCCESS)
    {   
      /*设置PLL时钟源及倍频系数    那种时钟源那种方式输入 倍频系数*/   
      RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //RCC_PLLMul_x(枚举2~16)是倍频值。当HSE=8MHZ,RCC_PLLMul_9时PLLCLK=72MHZ   
      /*设置AHB时钟(HCLK)*/   
      RCC_HCLKConfig(RCC_SYSCLK_Div1); //RCC_SYSCLK_Div1——AHB时钟 = 系统时钟(SYSCLK) = 72MHZ(外部晶振8HMZ)   
      /*注意此处的设置,如果使用SYSTICK做延时程序,此时SYSTICK(Cortex System timer)=HCLK/8=9MHZ*/   
      RCC_PCLK1Config(RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1),RCC_HCLK_Div2——APB1时钟 = HCLK/2 = 36MHZ(外部晶振8HMZ)   
      RCC_PCLK2Config(RCC_HCLK_Div1); //设置高速AHB时钟(PCLK2),RCC_HCLK_Div1——APB2时钟 = HCLK = 72MHZ(外部晶振8HMZ)   
      /*注:AHB主要负责外部存储器时钟。APB2负责AD,I/O,高级TIM,串口1。APB1负责DA,USB,SPI,I2C,CAN,串口2,3,4,5,普通TIM */  
      FLASH_SetLatency(FLASH_Latency_2); //设置FLASH存储器延时时钟周期数   
      /*FLASH时序延迟几个周期,等待总线同步操作。   
      推荐按照单片机系统运行频率:
      0—24MHz时,取Latency_0;   
      24—48MHz时,取Latency_1;   
      48~72MHz时,取Latency_2*/   
      FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //选择FLASH预取指缓存的模式,预取指缓存使能   
      RCC_PLLCmd(ENABLE);	//使能PLL
      while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //等待PLL输出稳定   
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //选择SYSCLK时钟源为PLL
      while(RCC_GetSYSCLKSource() != 0x08); //等待PLL成为SYSCLK时钟源   
    }  
    //开启需要使用的外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA |  RCC_APB2Periph_GPIOB 
                          |RCC_APB2Periph_GPIOC| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设时钟使能      
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //APB1外设时钟使能  
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);   	 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  
  }

mian.c

RCC_Configuration(); //系统时钟初始化

led.c

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);      //高速总线apb2上启动gpio a/b/c  

到了这里,关于STM32基础入门学习笔记:核心板 电路原理与驱动编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32基于CubeIDE和HAL库 基础入门学习笔记:物联网项目开发流程和思路

    文章目录: 第一部分:项目开始前的计划与准备 1.项目策划和开发规范  1.1 项目要求文档 1.2 技术实现文档 1.3 开发规范 2.创建项目工程与日志 第二部分:调通硬件电路与驱动程序 第三部分:编写最基础的应用程序 第四部分:完成最终要求、反复调试 第五部分:程序优化、

    2024年02月13日
    浏览(51)
  • STM32初学入门笔记(5):使用STM32CubeMX通过SPI,IIC驱动OLED屏幕

    随着时代的进步,OLED显示屏成为了继LCD显示屏之后的新一代显示屏技术,OLED具有可视角高,功耗低,厚度薄,耐冲击、振动能力强,像素响应时间低等优点,在嵌入式开发中,OLED显示器也是一个主要的部分,制作OLED显示模块的驱动也是学习STM32路上的重要一部分,本篇将从

    2024年02月04日
    浏览(43)
  • stm32HAL库学习笔记----pwm驱动舵机

    目录 一、目标 二、准备 三、原理 四、cubemx 五、程序 实现stm32驱动舵机旋转0°,45°,90°,135°,180°等角度。 stm32f103(c8t6),舵机(SG90 9g),杜邦线 接线:舵机红线-----------5V(mcu)            舵机棕线-----------GND(mcu)            舵机黄线-----------PA1(视情况而定

    2023年04月26日
    浏览(38)
  • STM32的一键下载CH340 DTR RTS与复位电路NRST的学习笔记

    这两天在学习stm32最小系统板的时候 对这一部分特别的不理解 于是就去找了很多东西去看  先说一键下载电路吧 先引用一张正点原子的原理图: 在芯片手册上查找ch340的手册,上面对于 RTS与DTR的定义是这样的 :   这就说明 我们是可以去控制RTS与DTR的一个高低电平的 从而

    2023年04月23日
    浏览(41)
  • 10、江科大stm32视频学习笔记——PWM驱动led呼吸灯、驱动舵机、驱动直流机

    目录 一、PWM驱动LED呼吸灯(灯接在PA0) 1、PWM波和GPIO的对应关系参考引脚定义表 2、计数器的计算 3、TIM输出PWM波使用步骤​编辑 4、代码 (1)输出化比较单元 (2) PWM.c (3)main.c 5、重映射更换成PA15亮灯 二、PWM驱动舵机(舵机接在PA1、按键在PB1)  1、电路图 2、参数计算

    2024年02月14日
    浏览(40)
  • 【正点原子STM32】RS485串行通信标准(串口基础协议 和 MODBUS协议、总线连接、通信电路、通信波形图、RS485相关HAL库驱动、RS485配置步骤、)

    一、RS485介绍 二、RS485相关HAL库驱动介绍 三、RS485配置步骤 四、编程实战 五、总结 串口、UART、TTL、RS232、RS422和RS485之间的关系可以如此理解: 串口 :是一个广义术语,通常指的是采用串行通信协议的接口,它可以包括多种具体的物理接口标准和逻辑电平标准。 UART (通用

    2024年04月13日
    浏览(69)
  • 小白入门STM32(2)---控制SG90舵机---基础工作原理详解

    本人一枚软件编程人员,有一定C语言基础,目前自学STM32单片机,写下此篇一巩固所学二交流分享。 按照惯例,先介绍理论再实战上手,穿插习题,最后给出我的愚见。 SG90属于一种舵机,最适合小白入门,可直接由单片机驱动。另外,单片机中给我们封装了很多东西,所以

    2023年04月10日
    浏览(44)
  • STM32CubeIDE学习笔记——使用HAL库PWM输出驱动舵机

    目录 PWM驱动简介 工程配置 代码编写 这里我采用的是STM32F103C8T6最小系统板,SG-90舵机实现功能。 舵机驱动角度和PWM占空比有关系,具体对应为50--0度  150--90度  250--180度,通过STM32的定时器功能输出PWM波来控制舵机进行转动。  时钟选择外部高速时钟 系统映射配置 时钟树设

    2024年02月13日
    浏览(43)
  • STM32之CubeMX学习笔记(7)SPI驱动OLED及其优化

    购买了逻辑分析仪后,总想着把所有的通讯信号全都看一遍。之前一篇笔记讲的是串口通信,做了一些小实验,搞清楚了如何基于底层利用串口传一些“非标”的数据。关于通信协议的第二篇,我想来看看SPI通信。 SPI通信是Serial peripheral interface的缩写,中文是串行外设接口,

    2024年02月04日
    浏览(71)
  • STM32进阶:使用STM32驱动ST7735S(内附核心源码)

    感觉很久很久没有来博客更新了,历经千难万阻,终于做出来了TFT显示屏的SPI驱动,这里分享以下核心源码,接下来一段时间开始准备考科一了,后面有时间了再来更新,有三种模式下的驱动。 实验环境:正点原子 STM32F103ZET6 小型系统板 实验工具:STM32F103ZET6 芯片与 ST7735S芯

    2024年02月01日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包