STC进阶开发(三)蜂鸣器、RTC时钟、I2C总线、外部中断、RTC闹钟设置、RTC计时器设置

前言

        这一期我们首先学习如何让蜂鸣器响起来,并且如何让蜂鸣器发出简单的歌曲,然后我们介绍RTC时钟,要想明白RTC时钟,我们还需要先介绍I2C总线和外部中断。接下来就开始这一期的学习吧!

蜂鸣器

简单介绍

        蜂鸣器是一种能够产生固定频率的声音的电子元件。它通常由振膜、震荡器、放大器和声音反馈电路等部分组成。振膜是蜂鸣器中最核心的部分,它能够将电信号转换为机械振动,产生声音。震荡器提供稳定的电信号,用于驱动振膜产生振动。放大器用于放大电信号的幅度,以便产生足够的声音。声音反馈电路可以提供反馈信号,帮助系统稳定。

原理图

分析:由原理图可以看到这里用到的是NPN型三极管,当电压为高电压时三极管导通,并且是由引脚P00控制的。

代码演示

简单发声

#include "Delay.h"
#include "GPIO.h"#define BUZZER P00void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_0;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_PP;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}void main(){GPIO_config();while(1){BUZZER = 1;delay_ms(1);BUZZER = 0;delay_ms(1);}
}

Timer测试发声

#include "GPIO.h"
#include "Delay.h"
#include "Timer.h"
#include "NVIC.h"#define BUZZER P00//小字二组: C`	   D`     E`   F`	  G`	A`	  B`    C``
u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_0;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_PP;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}void	Timer_config(u16 hz)
{TIM_InitTypeDef		TIM_InitStructure;						//结构定义//定时器0做16位自动重装, 中断频率为1000HZTIM_InitStructure.TIM_Mode      = TIM_16BitAutoReload;	//指定工作模式,   TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload,TIM_16BitAutoReloadNoMaskTIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T;		//指定时钟源,     TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_ExtTIM_InitStructure.TIM_ClkOut    = DISABLE;				//是否输出高速脉冲, ENABLE或DISABLE/*1. 这里使用  65536UL - (24M / 1000)  :  相当于是还要数24000下就数满,然后溢出触发中断函数2. MCU数24000 下,需要1ms时间,所以就等价于1ms触发一次中断函数。3. MAIN_Fosc / 1000 :  表示1ms 就触发一次中断,我们在中断里面完成蜂鸣器的发声和停止3.1 如果有1000次中断,那么就相当于是 发声的次数 + 停止的次数  = 1000次  ====> 500HZ3.2 如果有2000次中断,那么就相当于是 发声的次数 + 停止的次数  = 2000次  ====> 1000HZ3.3 如果有4000次中断,那么就相当于是 发声的次数 + 停止的次数  = 4000次  ====> 2000HZ*/TIM_InitStructure.TIM_Value     = 65536UL - (MAIN_Fosc / (hz * 2) );		//初值,TIM_InitStructure.TIM_Run       = ENABLE;				//是否初始化后启动定时器, ENABLE或DISABLETimer_Inilize(Timer0,&TIM_InitStructure);				//初始化Timer0	  Timer0,Timer1,Timer2,Timer3,Timer4NVIC_Timer0_Init(ENABLE,Priority_0);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}// 这是定时器的中断函数, 每隔1ms 就调用一次:: 表示来1000次这个函数,就有500个启动,500次停--->500HZ 
void timer0_handler(){/*1ms来第一次的时候,让蜂鸣器发声,下一个1ms来的时候就让它停止,再来下一个1ms就让它发声,下一个1ms来的时候就让它停止....*/BUZZER = ~BUZZER;
}void main(){u8 count = 0 ;//0. 中断总开关EA = 1;//1. IO模式GPIO_config();//2. 定时器配置 :: 不要在这里调用Timer的配置了,因为Timer的配置需要一个参数。参数还是变化的。//Timer_config();while(1){Timer_config(hz[count]);count++;if(count > 7){count = 0 ;}delay_ms(250);delay_ms(250);}
}

注:配置Timer初值是个需要注意的点,具体注释已经在代码中标明,请详细阅读。

PWM测试发声

#include "GPIO.h"
#include "Delay.h"
#include "STC8H_PWM.h"
#include "NVIC.h"
#include "Switch.h"#define BUZZER P00//小字二组: C`	   D`     E`   F`	  G`	A`	  B`    C``
u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_0;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_PP;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}//#define PERIOD  MAIN_Fosc / 1000void PWM_config(u16 hz){//周期时间// 分母是1000,即表示是1000HZ//u16 PERIOD = MAIN_Fosc / 1000;//如果你希望是多少hz, 那么就传递多少进来u16 PERIOD = MAIN_Fosc / hz;PWMx_InitDefine init;init.PWM_Mode = CCMRn_PWM_MODE2;			//模式,   CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2/*周期时间: 1. 上面使用时钟主频 / 1000 即表示 24000是一个周期,也就是1ms 是一个周期。2. 1s钟时间被切割成了1000份。 */init.PWM_Period = PERIOD - 1;		//周期时间,   0~65535/*1. 占空比表示高低电平的占空比:2. 想让蜂鸣器发声:BUZZER = 1 (高电平) , 想让蜂鸣器不发声: BUZZER = 0 (低电平)3. 我们希望在一个周期内(1ms)内有一半时间发声,一把时间不发生。4. 如果周期是经过 MAIN_Fosc / 1000 得到的 24000, 这里占空比是 24000 * 0.5 : 表示 1ms之内高电平占 50% 低电平占 50%  : 这是1000HZ?*/init.PWM_Duty = PERIOD * 0.5;			//占空比时间, 0~Periodinit.PWM_DeadTime = 0;	//死区发生器设置, 0~255init.PWM_EnoSelect = ENO5P;		//输出通道选择,	ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8Pinit.PWM_CEN_Enable = ENABLE;		//使能计数器, ENABLE,DISABLEinit.PWM_MainOutEnable = ENABLE;//主输出使能,  ENABLE,DISABLE//配置小分类 和 大分类PWM_Configuration(PWM5, &init );PWM_Configuration(PWMB, &init );//中断使能NVIC_PWM_Init(PWMB , DISABLE , Priority_1);//切换引脚PWM5_SW(PWM5_SW_P00);
}void main(){u8 count = 0 ;//0. 中断总开关EAXSFR();EA = 1;//1. IO模式GPIO_config();while(1){PWM_config(hz[count]);count++;if(count > 7){count = 0 ;}delay_ms(250);delay_ms(250);}
}

封装代码

main.c
#include "GPIO.h"
#include "Delay.h"
#include "BUZZER.h"//小字二组: C`	   D`     E`   F`	  G`	A`	  B`    C``
u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};void main(){u8 count = 0 ;//1. 初始化BUZZER_init();while(1){//PWM_config(hz[count]);BUZZER_play(hz[count]);count++;if(count > 7){count = 0 ;		//在停止前,必须确保当前的这个音还能正常的播放完 0.5s 的时间,才能去停止。delay_ms(250);delay_ms(250);//如果进入了这里,即表示已经播放完一轮次了。停止,歇一会。BUZZER_stop();//调用停止后,休息1.5秒。加上if后面的0.5秒 就相当于是停止了2秒钟,才去播放下一轮次。delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}delay_ms(250);delay_ms(250);}
}
BUZZER.h
#ifndef	__BUZZER_H
#define	__BUZZER_H
#include "STC8H_PWM.h"
#include "GPIO.h"#include "NVIC.h"
#include "Switch.h"//1. 放置宏#define BUZZER P00//2. 声明具体的功能函数void BUZZER_init();void BUZZER_play(u16 hz);void BUZZER_stop();#endif
BUZZER.c

#include "BUZZER.h"void ioconfig(){GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_0;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_PP;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}//1. 初始化
void BUZZER_init(){ioconfig();EAXSFR();
}//2. 播放声音,要求给进来具体的频率值
void BUZZER_play(u16 hz){//周期时间// 分母是1000,即表示是1000HZ//u16 PERIOD = MAIN_Fosc / 1000;//如果你希望是多少hz, 那么就传递多少进来u16 PERIOD = MAIN_Fosc / hz;PWMx_InitDefine init;init.PWM_Mode = CCMRn_PWM_MODE2;			//模式,   CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2/*周期时间: 1. 上面使用时钟主频 / 1000 即表示 24000是一个周期,也就是1ms 是一个周期。2. 1s钟时间被切割成了1000份。 */init.PWM_Period = PERIOD - 1;		//周期时间,   0~65535/*1. 占空比表示高低电平的占空比:2. 想让蜂鸣器发声:BUZZER = 1 (高电平) , 想让蜂鸣器不发声: BUZZER = 0 (低电平)3. 我们希望在一个周期内(1ms)内有一半时间发声,一把时间不发生。4. 如果周期是经过 MAIN_Fosc / 1000 得到的 24000, 这里占空比是 24000 * 0.5 : 表示 1ms之内高电平占 50% 低电平占 50%  : 这是1000HZ?*/init.PWM_Duty = PERIOD * 0.5;			//占空比时间, 0~Periodinit.PWM_DeadTime = 0;	//死区发生器设置, 0~255init.PWM_EnoSelect = ENO5P;		//输出通道选择,	ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8Pinit.PWM_CEN_Enable = ENABLE;		//使能计数器, ENABLE,DISABLEinit.PWM_MainOutEnable = ENABLE;//主输出使能,  ENABLE,DISABLE//配置小分类 和 大分类PWM_Configuration(PWM5, &init );PWM_Configuration(PWMB, &init );//中断使能NVIC_PWM_Init(PWMB , DISABLE , Priority_1);//切换引脚PWM5_SW(PWM5_SW_P00);
}//3. 停止播放
void BUZZER_stop(){PWMx_InitDefine init;init.PWM_EnoSelect = 0;		//输出通道选择,	ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8Pinit.PWM_CEN_Enable = DISABLE;		//使能计数器, ENABLE,DISABLEinit.PWM_MainOutEnable = DISABLE;//主输出使能,  ENABLE,DISABLEPWM_Configuration(PWM5, &init );PWM_Configuration(PWMB, &init );
}

播放两只老虎

BUZZER.h和BUZZER.c文件内容不变,只在main.c文件中改造。

#include "GPIO.h"
#include "Delay.h"
#include "UART.h"
#include "BUZZER.h"//小字二组[哆来咪发唆拉西哆]: C`	   D`     E`   F`	  G`	A`	  B`    C``
u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};// 这是两只老虎的音普
u8 notes[] = {1, 2, 3, 1,				1, 2, 3, 1,				3, 4, 5,		3, 4, 5,5, 6, 5, 4, 3, 1,		5, 6, 5, 4, 3, 1,		1, 5, 1,		1, 5, 1
};// 这是两只老虎的音长
u8 durations[] = {4, 4, 4, 4,				4, 4, 4, 4,				4, 4, 8,		4, 4, 8,3, 1, 3, 1, 4, 4,		3, 1, 3, 1, 4, 4,		4, 4, 8,		4, 4, 8
};//给定一个值,让它休息这么多时间
void delay_X_ms(u16 time){int i;while(--time){delay_ms(1);}//	for(i = 0 ; i < time ; i++){
//			delay_ms(1);
//	}}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void main(){u8 count = 0 ;int i = 0 , len = sizeof(notes) / sizeof(u8) ;//1. 初始化BUZZER_init();EA =1;UART_config();while(1){for(i = 0 ; i < len ; i++ ){//1. 播放什么音?//1.1 获取每一个音u8 note = notes[i];//1.2 根据音到赫兹数组里面取出它对应的频率u16 note_hz = hz[note - 1] ;//1.3 让蜂鸣器播放这个赫兹频率BUZZER_play(note_hz);//2. 这个音播放多少时间  休眠即可//2.1 同时取出来每一个音对应的音长:: 每一个音长就是100msdelay_X_ms(durations[i] * 100);}//播放完一次,停一下BUZZER_stop();delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}

RTC时钟

简单介绍

        RTC时钟是一种实时时钟芯片,通常与微控制器或计算机等设备配合使用,提供高精度的时间和日期信息,以便于设备进行时间相关的操作,如记录数据、定时执行任务、闹钟提醒等。我们开发板中采用的是PCF8563。

原理图

分析:

        先看RTC时钟原理图,P37引脚是接的外部中断,而我们用到的RTC时钟的读取数据或者写入数据我们用到的是P32和P33引脚,通过I2C协议进行数据的读取或者数据的写入。BT1是一个外部电源,保证单片机断电后时钟仍然能继续工作。I2C我们下面一小章节介绍。

        数据是怎么读取或者写入时钟的呢,这里我们就要查阅PCF8563的数据手册了。

可以看到秒,分钟小时,日等都是BCD格式存储的,也就是它的高四位是数据的十位,第四位是数据的个位。所以我们写代码的时候要进行转化。

代码演示

RTC读取数据
#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_2| GPIO_Pin_3;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_OD;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void I2C_config() {I2C_InitTypeDef init;/*I2C总线支持的总线速度是: 100K ~ 400K400K = 24M/2/(Speed*2+4)400  = 24000 /2 / (Speed*2+4)4 = 240 / 2 / (Speed*2+4)4 = 120 / (Speed*2+4)(Speed*2+4) = 30  ===> Speed*2 = 26   ===> Speed = 13*/init.I2C_Speed = 13;				// 总线速度=Fosc/2/(Speed*2+4),      0~63init.I2C_Enable = ENABLE;				//I2C功能使能,   ENABLE, DISABLEinit.I2C_Mode = I2C_Mode_Master;					//主从模式选择,  I2C_Mode_Master,I2C_Mode_Slaveinit.I2C_MS_WDTA = DISABLE;				//主机使能自动发送,  ENABLE, DISABLE// 初始化I2C_Init(&init);//中断使能NVIC_I2C_Init(I2C_Mode_Master, DISABLE, Priority_1);//切换引脚I2C_SW(I2C_P33_P32);
}void main() {u8 second =0, minute=0 , hour =0, day =30, month=0 ,  C = 0 , week = 0 ;int year = 0 ;//I2C 总线从地址:读,0A3H;写,0A2Hu8 dev_addr = 0xA2 ;//表示给秒的寄存器地址进去,希望读取秒的数据u8 mem_addr = 0x02 ;//存数据,给一个数组 :: 0-秒 1-分 2-时 3-日 4-星期 5-月/世纪 6-年u8 dat[7] ;//就读一个长度u8 number = 7;//0. 总开关EA = 1 ;EAXSFR();//1. IO模式GPIO_config();//2. 串口配置UART_config();//3. I2C配置I2C_config();while(1) {/* 1. 读取时钟芯片里面的时间数据2. 参数解释:2.1 参数一: 设备地址,RTC时钟芯片的地址。2.2 参数二: 具体读取的数据对应的寄存器地址2.3 参数三: 用来收取数据的地址。一般会声明一个数组出来。 一般是长度为7的数组 【年月日时分秒星期】2.4 参数四: 表示要读取几个数据*/I2C_ReadNbyte(dev_addr, mem_addr, dat , number);//解析数据 //获取秒second = ((dat[0] >> 4) & 0x07 ) * 10  +  (dat[0] & 0x0F);//获取分minute = (( dat[1] >> 4 ) & 0x07 ) * 10 + (dat[1] & 0x0F);//获取时hour =  ((dat[2] >> 4 ) & 0x03) * 10   +  (dat[2] & 0x0F);//获取日day =  ((dat[3] >> 4 ) & 0x03) * 10   +  (dat[3] & 0x0F);//获取星期week = dat[4] & 0x07 ;//获取月month = (( dat[5] >> 4 ) & 0x01 ) * 10 + (dat[5] & 0x0F);/*获取年1. 年份的值有 千位 +  百位 +  十位 +  个位 构成2. 但是在年的寄存器里面的只有 十位和个位的值。缺少千位和百位的值3. 需要去判定月份的第7位的值,由它来决定千位和百位的值3.1 如果一会月份的第7位 (C) 是 0 :  2000 + 年的数据   2000 ~ 20993.2 如果一会月份的第7位 (C) 是 1 :  2100 + 年的数据   2100 ~ 2199 月份:  ?111 11111000 0000-------------0000 00001000 0000*/// 得到1 或者的到 0 C = (dat[5] & 0x80) > 0 ? 1 : 0 ;//年份的数据需要配合前面的千位和百位year = ((dat[6] >> 4) & 0x0F) * 10   + (dat[6] & 0x0F)  ;//这里是为了判定是1 还是 0 if(C == 1){year += 2100;}else{year += 2000;}printf("%d-%d-%d %d:%d:%d \n" ,  (int)year , (int)month , (int)day , (int)hour , (int)minute , (int)second );printf("week=%d \n" , week);//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}
RTC写入数据(数组封装)
#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_2| GPIO_Pin_3;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_OD;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void I2C_config() {I2C_InitTypeDef init;/*I2C总线支持的总线速度是: 100K ~ 400K400K = 24M/2/(Speed*2+4)400  = 24000 /2 / (Speed*2+4)4 = 240 / 2 / (Speed*2+4)4 = 120 / (Speed*2+4)(Speed*2+4) = 30  ===> Speed*2 = 26   ===> Speed = 13*/init.I2C_Speed = 13;				// 总线速度=Fosc/2/(Speed*2+4),      0~63init.I2C_Enable = ENABLE;				//I2C功能使能,   ENABLE, DISABLEinit.I2C_Mode = I2C_Mode_Master;					//主从模式选择,  I2C_Mode_Master,I2C_Mode_Slaveinit.I2C_MS_WDTA = DISABLE;				//主机使能自动发送,  ENABLE, DISABLE// 初始化I2C_Init(&init);//中断使能NVIC_I2C_Init(I2C_Mode_Master, DISABLE, Priority_1);//切换引脚I2C_SW(I2C_P33_P32);
}#define DECIMAL2BCD(i) ((( i / 10 ) << 4)  |  (i % 10))void main() {u8 new_year ;u8 second =55, minute=59 , hour =23, day =31, month=12 ,  C = 1 , week = 3 ;int year = 2023 ;//I2C 总线从地址:读,0A3H;写,0A2Hu8 dev_addr = 0xA2 ;//表示给秒的寄存器地址进去,希望读取秒的数据u8 mem_addr = 0x02 ;//存数据,给一个数组 :: 0-秒 1-分 2-时 3-日 4-星期 5-月/世纪 6-年u8 dat[7] ;//就读一个长度u8 number = 7;//0. 总开关EA = 1 ;EAXSFR();//1. IO模式GPIO_config();//2. 串口配置UART_config();//3. I2C配置I2C_config();//4. 写入时间//写入秒 (BCD格式) : 十位 +  个位dat[0]  = DECIMAL2BCD(second) ;//写入分 (BCD格式) : 十位 +  个位  39dat[1] = DECIMAL2BCD(minute) ;//写入时 (BCD格式) : 十位 +  个位dat[2] = DECIMAL2BCD(hour) ;//写入日  (BCD格式) : 十位 +  个位dat[3] = DECIMAL2BCD(day) ;//写入星期 (BCD格式) : 十位 +  个位dat[4] = DECIMAL2BCD(week);//写入月 (BCD格式) : 十位 +  个位//月份寄存器里面不光有月份的数据,也有世纪的数据【世纪的数据影响着后面的年份的数据】//先准备月份的数据dat[5] = DECIMAL2BCD(month) ;  // 0001 0010//再添加世纪的数据到月份的字节里面:修改月份的最高位 【第7位】,看C是什么? C是0,月份的第7位就是0 ,C是1 ,月份的第7位就是1if(C == 0){ // C是0,月份的第7位就是0dat[5] &= ~(1 << 7);}else{ // C是1 ,月份的第7位就是1dat[5] |= (1 << 7);}//写入年 (BCD格式) : 十位 +  个位'// warning C182: pointer to different objects//比如年份是2023,先得到23这个值,new_year = year % 100;  // 23//再转换成 (BCD格式) : 十位 +  个位dat[6] = DECIMAL2BCD(new_year) ;//写数据I2C_WriteNbyte(dev_addr,mem_addr , dat , 7);while(1) {/* 1. 读取时钟芯片里面的时间数据2. 参数解释:2.1 参数一: 设备地址,RTC时钟芯片的地址。2.2 参数二: 具体读取的数据对应的寄存器地址2.3 参数三: 用来收取数据的地址。一般会声明一个数组出来。 一般是长度为7的数组 【年月日时分秒星期】2.4 参数四: 表示要读取几个数据*/I2C_ReadNbyte(dev_addr, mem_addr, dat , number);#define BCD2DECIMAL(a , b)  (((a >> 4) & b ) * 10  +  (a & 0x0F))//解析数据 //获取秒//second = ((dat[0] >> 4) & 0x07 ) * 10  +  (dat[0] & 0x0F);second = BCD2DECIMAL(dat[0] , 0x07);//获取分//minute = (( dat[1] >> 4 ) & 0x07 ) * 10 + (dat[1] & 0x0F);minute = BCD2DECIMAL(dat[1] , 0x07);//获取时//hour =  ((dat[2] >> 4 ) & 0x03) * 10   +  (dat[2] & 0x0F);hour = BCD2DECIMAL(dat[2] , 0x03);//获取日//day =  ((dat[3] >> 4 ) & 0x03) * 10   +  (dat[3] & 0x0F);day = BCD2DECIMAL(dat[3] , 0x03);//获取星期week = dat[4] & 0x07 ;//获取月//month = (( dat[5] >> 4 ) & 0x01 ) * 10 + (dat[5] & 0x0F);month = BCD2DECIMAL(dat[5] , 0x01);/*获取年1. 年份的值有 千位 +  百位 +  十位 +  个位 构成2. 但是在年的寄存器里面的只有 十位和个位的值。缺少千位和百位的值3. 需要去判定月份的第7位的值,由它来决定千位和百位的值3.1 如果一会月份的第7位 (C) 是 0 :  2000 + 年的数据   2000 ~ 20993.2 如果一会月份的第7位 (C) 是 1 :  2100 + 年的数据   2100 ~ 2199 月份:  ?111 11111000 0000-------------0000 00001000 0000*/// 得到1 或者的到 0 C = (dat[5] & 0x80) > 0 ? 1 : 0 ;//年份的数据需要配合前面的千位和百位//year = ((dat[6] >> 4) & 0x0F) * 10   + (dat[6] & 0x0F)  ;year = BCD2DECIMAL(dat[6] , 0x0F);printf("year=%d\n" , year);//这里是为了判定是1 还是 0 if(C == 1){year += 2100;}else{year += 2000;}printf("%d-%d-%d %d:%d:%d \n" ,  (int)year , (int)month , (int)day , (int)hour , (int)minute , (int)second );printf("week=%d \n" , (int)week);//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}
RTC封装
main.c
#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"
#include "RTC.h"void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void main() {RTC_Time write_time;//0. 总开关EA = 1 ;//1. RTC初始化RTC_init();//2. 串口配置UART_config();//写入时间write_time.year=2023;write_time.month=10;write_time.day=31;write_time.hour=23;write_time.minute=59;write_time.second=50;write_time.week=5;RTC_WriteTime(&write_time);while(1) {//读取时间RTC_Time time;RTC_ReadTime(&time);printf("%d-%d-%d %d:%d:%d\n" , time.year, (int)time.month, (int)time.day , (int)time.hour, (int)time.minute, (int)time.second );printf("week=%d\n" , (int)time.week);//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}
RTC.h
#ifndef	__RTC_H
#define	__RTC_H#include "GPIO.h"
#include "NVIC.h"
#include "I2C.h"
#include "Switch.h"/*1. 每一个支持I2C协议元件,手册都会提供两个地址: 读地址和写地址2. 这两个地址都不是设备地址,但是他们是经过设备地址变化得来的。3. 读地址和写地址,需要用到设备地址来扮演他们的高7位,它们的第0位,设置成 0 或者是 1就可以描述现在要和这个设备做什么动作: 写的动作? 读的动作写地址: 0xA2 : 1010 0010读地址: 0xA3 : 1010 0011得到设备地址:0101 0001  ---> 0x51从设备地址得到写的地址:  0x51 << 1 ==右边补0==> 1010 0010从设备地址得到读的地址:  (0x51 << 1) |1 = 1010 0011
*///1. 宏、结构体
#define DECIMAL2BCD(i) ((( i / 10 ) << 4)  |  (i % 10))
#define BCD2DECIMAL(a , b)  (((a >> 4) & b ) * 10  +  (a & 0x0F))#define DEV_ADDRESS 0xA2  // 这里不要被迷惑了,放的应该是设备的地址 + 写的动作  =  写地址
#define MEM_ADDRESS 0x02
#define NUMBER 7//I2C 总线从地址:读,0A3H;写,0A2H
//u8 dev_addr = 0xA2 ;//表示给秒的寄存器地址进去,希望读取秒的数据
//u8 mem_addr = 0x02 ;typedef struct
{u8 second , minute , hour, day, month, week;int year ;
} RTC_Time;//2. 功能函数声明//2.1 配置、初始化
void RTC_init();//2.2 具体的功能:// 读取时间
void RTC_ReadTime(RTC_Time * time);//写入时间
void RTC_WriteTime(RTC_Time * time);#endif
RTC.c
#include "RTC.h"void IO_config(){GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_2| GPIO_Pin_3;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_OD;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void I2C_config(){I2C_InitTypeDef init;/*I2C总线支持的总线速度是: 100K ~ 400K400K = 24M/2/(Speed*2+4)400  = 24000 /2 / (Speed*2+4)4 = 240 / 2 / (Speed*2+4)4 = 120 / (Speed*2+4)(Speed*2+4) = 30  ===> Speed*2 = 26   ===> Speed = 13*/init.I2C_Speed = 13;				// 总线速度=Fosc/2/(Speed*2+4),      0~63init.I2C_Enable = ENABLE;				//I2C功能使能,   ENABLE, DISABLEinit.I2C_Mode = I2C_Mode_Master;					//主从模式选择,  I2C_Mode_Master,I2C_Mode_Slaveinit.I2C_MS_WDTA = DISABLE;				//主机使能自动发送,  ENABLE, DISABLE// 初始化I2C_Init(&init);//中断使能NVIC_I2C_Init(I2C_Mode_Master, DISABLE, Priority_1);//切换引脚I2C_SW(I2C_P33_P32);
}//配置、初始化
void RTC_init(){//打开外部寄存器使能EAXSFR();EA = 1;//基础配置IO_config();I2C_config();}// 读取时间
void RTC_ReadTime(RTC_Time * time){// 事先准备一个数组,让I2C把时间数据读取到这个数组里面来u8 dat[7];/* 1. 读取时钟芯片里面的时间数据2. 参数解释:2.1 参数一: 设备地址,RTC时钟芯片的地址。2.2 参数二: 具体读取的数据对应的寄存器地址2.3 参数三: 用来收取数据的地址。一般会声明一个数组出来。 一般是长度为7的数组 【年月日时分秒星期】2.4 参数四: 表示要读取几个数据*/I2C_ReadNbyte(DEV_ADDRESS, MEM_ADDRESS, dat , NUMBER);//解析数据 //获取秒//second = ((dat[0] >> 4) & 0x07 ) * 10  +  (dat[0] & 0x0F);time->second = BCD2DECIMAL(dat[0] , 0x07);//方式一: 对结构体指针的内部成员赋值//(*time).second = BCD2DECIMAL(dat[0] , 0x07);//方式二: 直接使用指针 配合 -> 对成员赋值//time->second = BCD2DECIMAL(dat[0] , 0x07);//获取分//minute = (( dat[1] >> 4 ) & 0x07 ) * 10 + (dat[1] & 0x0F);time->minute = BCD2DECIMAL(dat[1] , 0x07);//获取时//hour =  ((dat[2] >> 4 ) & 0x03) * 10   +  (dat[2] & 0x0F);time->hour = BCD2DECIMAL(dat[2] , 0x03);//获取日//day =  ((dat[3] >> 4 ) & 0x03) * 10   +  (dat[3] & 0x0F);time->day = BCD2DECIMAL(dat[3] , 0x03);//获取星期time->week = dat[4] & 0x07 ;//获取月//month = (( dat[5] >> 4 ) & 0x01 ) * 10 + (dat[5] & 0x0F);time->month = BCD2DECIMAL(dat[5] , 0x01);/*获取年1. 年份的值有 千位 +  百位 +  十位 +  个位 构成2. 但是在年的寄存器里面的只有 十位和个位的值。缺少千位和百位的值3. 需要去判定月份的第7位的值,由它来决定千位和百位的值3.1 如果一会月份的第7位 (C) 是 0 :  2000 + 年的数据   2000 ~ 20993.2 如果一会月份的第7位 (C) 是 1 :  2100 + 年的数据   2100 ~ 2199 月份:  ?111 11111000 0000-------------0000 00001000 0000*/// 得到1 或者的到 0 //C = (dat[5] & 0x80) > 0 ? 1 : 0 ;//年份的数据需要配合前面的千位和百位//year = ((dat[6] >> 4) & 0x0F) * 10   + (dat[6] & 0x0F)  ;time->year = BCD2DECIMAL(dat[6] , 0x0F);//这里是为了判定是1 还是 0 if(dat[5] & 0x80){time->year += 2100;}else{time->year += 2000;}
}//写入时间
void RTC_WriteTime(RTC_Time * time){u8 dat[7]; u8 new_year;//写入秒 (BCD格式) : 十位 +  个位dat[0]  = DECIMAL2BCD(time->second) ;//写入分 (BCD格式) : 十位 +  个位  39dat[1] = DECIMAL2BCD(time->minute) ;//写入时 (BCD格式) : 十位 +  个位dat[2] = DECIMAL2BCD(time->hour) ;//写入日  (BCD格式) : 十位 +  个位dat[3] = DECIMAL2BCD(time->day) ;//写入星期 (BCD格式) : 十位 +  个位dat[4] = DECIMAL2BCD(time->week);//写入月 (BCD格式) : 十位 +  个位//月份寄存器里面不光有月份的数据,也有世纪的数据【世纪的数据影响着后面的年份的数据】//先准备月份的数据dat[5] = DECIMAL2BCD(time->month) ;  // 0001 0010//再添加世纪的数据到月份的字节里面:修改月份的最高位 【第7位】,看C是什么? C是0,月份的第7位就是0 ,C是1 ,月份的第7位就是1//关于世纪C的赋值,要参考年份的数据,如果是20xx年,那么C就赋值 0 , 如果是 21xx年,那么C就赋值 1if(time->year >= 2100){ // 如果是 21xx年,那么C就赋值 1dat[5] |= (1 << 7);}else{ // 如果是20xx年,那么C就赋值 0 dat[5] &= ~(1 << 7);}//if(C == 0){ // C是0,月份的第7位就是0//   dat[5] &= ~(1 << 7);//}else{ // C是1 ,月份的第7位就是1//   dat[5] |= (1 << 7);//}//写入年 (BCD格式) : 十位 +  个位'// warning C182: pointer to different objects//比如年份是2023,先得到23这个值,new_year = time->year % 100;  // 23//再转换成 (BCD格式) : 十位 +  个位dat[6] = DECIMAL2BCD(new_year) ;//写数据I2C_WriteNbyte(DEV_ADDRESS,MEM_ADDRESS , dat , NUMBER);
}

I2C总线

简单介绍

        I2C(Inter-Integrated Circuit)是一种串行通信协议,用于在集成电路之间进行数据交换。它最初由飞利浦公司(Philips)开发,现已成为一种通用的串行通信协议,被广泛应用于各种电子设备和嵌入式系统中。

总线结构

        I2C总线包括两根信号线:SDA(串行数据线)和SCL(串行时钟线)。这两根信号线共用一个总线,因此在总线上可以连接多个设备。在I2C总线上,每个设备都有一个唯一的地址,用于标识设备。

        SCL线是时钟线,用于控制数据传输的速度和时序;SDA线是数据线,用于传输实际的数据.

设备的地址通常是由设备制造商确定的,并在设备的数据手册中公布。

  1. Master: 主设备。通常是主控MCU
  2. Slave:从设备。通常是功能芯片,例如RTC时钟,陀螺仪,温湿度等等。
  3. SCL:时钟线,控制数据传输的速度和时序。
  4. SDA:数据线。传输数据的。
  5. 地址:从设备地址。主设备通过地址进行访问。在总线中,每个从设备地址唯一。

STC8H内置了一组I2C接口

I2C接口

SCL

SDA

I2C1

P1.5

P1.4

P2.5

P2.4

P3.2

P3.3

外部中断(EXTI)

简单介绍

外部中断

引脚

备注

INT0

P3.2

支持上升沿和下降沿中断

INT1

P3.3

支持上升沿和下降沿中断

INT2

P3.6

只支持下降沿中断

INT3

P3.7

只支持下降沿中断

INT4

P3.0

只支持下降沿中断

在数字电路中,信号的电平变化分为上升沿和下降沿。

上升沿指的是信号从低电平变为高电平的瞬间,下降沿指的是信号从高电平变为低电平的瞬间。

例如,当一个开关被按下时,电路中的信号从低电平变为高电平,此时发生了一个上升沿;当开关被松开时,信号从高电平变为低电平,此时发生了一个下降沿。在数字电路中,上升沿和下降沿往往会被用作时序控制和数据传输等方面的参考信号。

代码演示

#include "GPIO.h"
#include "NVIC.h"
#include "UART.h"
#include "Switch.h"
#include "Exti.h"
void GPIO_config(void) {//INT0GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_2);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void EXTI_config(){//创建结构体变量EXTI_InitTypeDef init;//给成员赋值: 配置中断触发模式: 上升沿和下降沿触发还是只有下降沿触发init.EXTI_Mode = EXT_MODE_RiseFall; //中断模式,  	EXT_MODE_RiseFall, EXT_MODE_Fall//初始化Ext_Inilize(EXT_INT0, &init);//中断使能NVIC_INT0_Init(ENABLE , Priority_1);
}//如果触发了外部中断0,那么就执行这个函数
void INT0_handler(){if(P32){printf("rise...\n"); //上升沿}else{printf("fall...\n"); //下降沿}
}void main(){EA = 1 ;//1. IOGPIO_config();//2. 串口UART_config();//3. 外部中断EXTI_config();while(1){}
}

RTC闹钟设置

读取数据手册

介绍:地址为01H的寄存器有八个字节,TIE和TF为一组,用于控制是否进行计数器中断,AIE和AF为一组,用于控制是否进行闹钟中断。设置闹钟时,将AIE置为1,表示中断有效,当执行中断后,AF会自动置为1,并且不会重置,所以我们如果下次还要继续产生中断我们就必须在完成第一次中断后手动将AF置为0,确保下次中断能发生。TIE和TF使用原理与AF相同。

那么我们要怎么设置闹钟呢?我们要将数据写入哪个寄存器呢?

我们对地址为09H的报警计时器也就是闹钟计时器进行操作,这个寄存器的第7位置为1时代表报警无效,当他置为0时报警有效。0~6位存储要设置的数据,但仍然是BCD形式存储,所以我们要对齐进行转化。在操作工程中我们要将寄存器中的数据先读取出来,对其修改后在写入进去,从而对闹钟进行设置。

代码演示

RTC闹钟设置

#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"
#include "RTC.h"
#include "Exti.h"void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_7;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_2);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void EXTI_config(){EXTI_InitTypeDef init;init.EXTI_Mode = EXT_MODE_RiseFall;			//中断模式,  	EXT_MODE_RiseFall, EXT_MODE_FallExt_Inilize( EXT_INT3 , &init);//中断使能NVIC_INT3_Init(ENABLE,Priority_1);
}// RTC时钟的中断处理: 闹钟和定时器发生中断都会调用这个函数
void RTC_ISR_Handle(){printf("tttt...\n");}void main() {u8 alarm[4];u8 dat , minute ;RTC_Time write_time;//0. 总开关EA = 1 ;GPIO_config();//1. RTC初始化RTC_init();//2. 串口配置UART_config();//配置外部中断EXTI_config();//写入时间write_time.year=2023;write_time.month=10;write_time.day=31;write_time.hour=13;write_time.minute=55;write_time.second=57;write_time.week=5;RTC_WriteTime(&write_time);//==================================闹钟设置 :: begin=============================//1. 设置闹钟的总开关:允许闹钟中断//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改过后,//a. 允许闹钟中断:: 把第1位置1dat |= 0x02;//b. 清除闹钟发生过的标记 :: 把 第3位 置0dat &= ~0x08; //1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);//2. 设置具体是什么时间触发闹钟。 30//2.1 时间必须是BCD格式,并且最高位第7位要置 0 //minute = DECIMAL2BCD(56) & ~0x80;alarm[0] = DECIMAL2BCD(56) & 0x7F; //分alarm[1] = DECIMAL2BCD(13) & 0x7F; // 时alarm[2] = DECIMAL2BCD(31) & 0x7F; // 日alarm[3] = DECIMAL2BCD(5) & 0x7F; // 星期//2.2 把分钟时间设置进去I2C_WriteNbyte(0xA2, 0x09, alarm, 4);//==================================闹钟设置 :: end=============================while(1) {//读取时间RTC_Time time;RTC_ReadTime(&time);printf("%d-%d-%d %d:%d:%d\n" , time.year, (int)time.month, (int)time.day , (int)time.hour, (int)time.minute, (int)time.second );printf("week=%d\n" , (int)time.week);//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}

RTC闹钟设置(封装)

main.c
#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"
#include "RTC.h"
#include "Exti.h"void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_7;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_2);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void EXTI_config(){EXTI_InitTypeDef init;init.EXTI_Mode = EXT_MODE_RiseFall;			//中断模式,  	EXT_MODE_RiseFall, EXT_MODE_FallExt_Inilize( EXT_INT3 , &init);//中断使能NVIC_INT3_Init(ENABLE,Priority_1);
}// RTC时钟的中断处理: 闹钟和定时器发生中断都会调用这个函数
void RTC_ISR_Handle(){printf("tttt...\n");//来到这里之后,为了让下一次的闹钟到点了还能来这里,需要手动清理标记为RTC_Alarm_Clear_Flag();}void main() {RTC_Time write_time;RTC_Alarm alarm;//0. 总开关EA = 1 ;GPIO_config();//1. RTC初始化RTC_init();//2. 串口配置UART_config();//配置外部中断EXTI_config();//写入时间write_time.year=2023;write_time.month=10;write_time.day=31;write_time.hour=13;write_time.minute=55;write_time.second=57;write_time.week=5;RTC_WriteTime(&write_time);//设置闹钟alarm.minute = 56;  //56分的时候响闹钟alarm.minute_enable = 1;  // 启用分钟闹钟RTC_Alarm_Start(alarm);while(1) {//读取时间RTC_Time time;RTC_ReadTime(&time);printf("%d-%d-%d %d:%d:%d\n" , time.year, (int)time.month, (int)time.day , (int)time.hour, (int)time.minute, (int)time.second );printf("week=%d\n" , (int)time.week);//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}
RTC.h
#ifndef	__RTC_H
#define	__RTC_H#include "GPIO.h"
#include "NVIC.h"
#include "I2C.h"
#include "Switch.h"/*1. 每一个支持I2C协议元件,手册都会提供两个地址: 读地址和写地址2. 这两个地址都不是设备地址,但是他们是经过设备地址变化得来的。3. 读地址和写地址,需要用到设备地址来扮演他们的高7位,它们的第0位,设置成 0 或者是 1就可以描述现在要和这个设备做什么动作: 写的动作? 读的动作写地址: 0xA2 : 1010 0010读地址: 0xA3 : 1010 0011得到设备地址:0101 0001  ---> 0x51从设备地址得到写的地址:  0x51 << 1 ==右边补0==> 1010 0010从设备地址得到读的地址:  (0x51 << 1) |1 = 1010 0011
*///1. 宏、结构体
#define DECIMAL2BCD(i) ((( i / 10 ) << 4)  |  (i % 10))
#define BCD2DECIMAL(a , b)  (((a >> 4) & b ) * 10  +  (a & 0x0F))#define DEV_ADDRESS 0xA2  // 这里不要被迷惑了,放的应该是设备的地址 + 写的动作  =  写地址
#define MEM_ADDRESS 0x02
#define NUMBER 7//I2C 总线从地址:读,0A3H;写,0A2H
//u8 dev_addr = 0xA2 ;//表示给秒的寄存器地址进去,希望读取秒的数据
//u8 mem_addr = 0x02 ;typedef struct
{u8 second , minute , hour, day, month, week;int year ;
} RTC_Time;typedef struct
{u8  minute, hour, day,  week ;u8  minute_enable , hour_enable ,  day_enable , week_enable;
} RTC_Alarm;//2. 功能函数声明//2.1 配置、初始化
void RTC_init();//2.2 具体的功能://====================时间功能函数===============================
// 读取时间
void RTC_ReadTime(RTC_Time * time);//写入时间
void RTC_WriteTime(RTC_Time * time);//====================闹钟功能函数===============================//设置闹钟(开始闹钟)
void RTC_Alarm_Start(RTC_Alarm alarm);//停止闹钟(不设置闹钟了...)
void RTC_Alarm_Stop();// 清除闹钟发生过的标记,以便闹钟在一次到点了还能触发
void RTC_Alarm_Clear_Flag();#endif
RTC.c
#include "RTC.h"void IO_config(){GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_2| GPIO_Pin_3;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_OD;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void I2C_config(){I2C_InitTypeDef init;/*I2C总线支持的总线速度是: 100K ~ 400K400K = 24M/2/(Speed*2+4)400  = 24000 /2 / (Speed*2+4)4 = 240 / 2 / (Speed*2+4)4 = 120 / (Speed*2+4)(Speed*2+4) = 30  ===> Speed*2 = 26   ===> Speed = 13*/init.I2C_Speed = 13;				// 总线速度=Fosc/2/(Speed*2+4),      0~63init.I2C_Enable = ENABLE;				//I2C功能使能,   ENABLE, DISABLEinit.I2C_Mode = I2C_Mode_Master;					//主从模式选择,  I2C_Mode_Master,I2C_Mode_Slaveinit.I2C_MS_WDTA = DISABLE;				//主机使能自动发送,  ENABLE, DISABLE// 初始化I2C_Init(&init);//中断使能NVIC_I2C_Init(I2C_Mode_Master, DISABLE, Priority_1);//切换引脚I2C_SW(I2C_P33_P32);
}//配置、初始化
void RTC_init(){//打开外部寄存器使能EAXSFR();EA = 1;//基础配置IO_config();I2C_config();}// 读取时间
void RTC_ReadTime(RTC_Time * time){// 事先准备一个数组,让I2C把时间数据读取到这个数组里面来u8 dat[7];/* 1. 读取时钟芯片里面的时间数据2. 参数解释:2.1 参数一: 设备地址,RTC时钟芯片的地址。2.2 参数二: 具体读取的数据对应的寄存器地址2.3 参数三: 用来收取数据的地址。一般会声明一个数组出来。 一般是长度为7的数组 【年月日时分秒星期】2.4 参数四: 表示要读取几个数据*/I2C_ReadNbyte(DEV_ADDRESS, MEM_ADDRESS, dat , NUMBER);//解析数据 //获取秒//second = ((dat[0] >> 4) & 0x07 ) * 10  +  (dat[0] & 0x0F);time->second = BCD2DECIMAL(dat[0] , 0x07);//方式一: 对结构体指针的内部成员赋值//(*time).second = BCD2DECIMAL(dat[0] , 0x07);//方式二: 直接使用指针 配合 -> 对成员赋值//time->second = BCD2DECIMAL(dat[0] , 0x07);//获取分//minute = (( dat[1] >> 4 ) & 0x07 ) * 10 + (dat[1] & 0x0F);time->minute = BCD2DECIMAL(dat[1] , 0x07);//获取时//hour =  ((dat[2] >> 4 ) & 0x03) * 10   +  (dat[2] & 0x0F);time->hour = BCD2DECIMAL(dat[2] , 0x03);//获取日//day =  ((dat[3] >> 4 ) & 0x03) * 10   +  (dat[3] & 0x0F);time->day = BCD2DECIMAL(dat[3] , 0x03);//获取星期time->week = dat[4] & 0x07 ;//获取月//month = (( dat[5] >> 4 ) & 0x01 ) * 10 + (dat[5] & 0x0F);time->month = BCD2DECIMAL(dat[5] , 0x01);/*获取年1. 年份的值有 千位 +  百位 +  十位 +  个位 构成2. 但是在年的寄存器里面的只有 十位和个位的值。缺少千位和百位的值3. 需要去判定月份的第7位的值,由它来决定千位和百位的值3.1 如果一会月份的第7位 (C) 是 0 :  2000 + 年的数据   2000 ~ 20993.2 如果一会月份的第7位 (C) 是 1 :  2100 + 年的数据   2100 ~ 2199 月份:  ?111 11111000 0000-------------0000 00001000 0000*/// 得到1 或者的到 0 //C = (dat[5] & 0x80) > 0 ? 1 : 0 ;//年份的数据需要配合前面的千位和百位//year = ((dat[6] >> 4) & 0x0F) * 10   + (dat[6] & 0x0F)  ;time->year = BCD2DECIMAL(dat[6] , 0x0F);//这里是为了判定是1 还是 0 if(dat[5] & 0x80){time->year += 2100;}else{time->year += 2000;}
}//写入时间
void RTC_WriteTime(RTC_Time * time){u8 dat[7]; u8 new_year;//写入秒 (BCD格式) : 十位 +  个位dat[0]  = DECIMAL2BCD(time->second) ;//写入分 (BCD格式) : 十位 +  个位  39dat[1] = DECIMAL2BCD(time->minute) ;//写入时 (BCD格式) : 十位 +  个位dat[2] = DECIMAL2BCD(time->hour) ;//写入日  (BCD格式) : 十位 +  个位dat[3] = DECIMAL2BCD(time->day) ;//写入星期 (BCD格式) : 十位 +  个位dat[4] = DECIMAL2BCD(time->week);//写入月 (BCD格式) : 十位 +  个位//月份寄存器里面不光有月份的数据,也有世纪的数据【世纪的数据影响着后面的年份的数据】//先准备月份的数据dat[5] = DECIMAL2BCD(time->month) ;  // 0001 0010//再添加世纪的数据到月份的字节里面:修改月份的最高位 【第7位】,看C是什么? C是0,月份的第7位就是0 ,C是1 ,月份的第7位就是1//关于世纪C的赋值,要参考年份的数据,如果是20xx年,那么C就赋值 0 , 如果是 21xx年,那么C就赋值 1if(time->year >= 2100){ // 如果是 21xx年,那么C就赋值 1dat[5] |= (1 << 7);}else{ // 如果是20xx年,那么C就赋值 0 dat[5] &= ~(1 << 7);}//if(C == 0){ // C是0,月份的第7位就是0//   dat[5] &= ~(1 << 7);//}else{ // C是1 ,月份的第7位就是1//   dat[5] |= (1 << 7);//}//写入年 (BCD格式) : 十位 +  个位'// warning C182: pointer to different objects//比如年份是2023,先得到23这个值,new_year = time->year % 100;  // 23//再转换成 (BCD格式) : 十位 +  个位dat[6] = DECIMAL2BCD(new_year) ;//写数据I2C_WriteNbyte(DEV_ADDRESS,MEM_ADDRESS , dat , NUMBER);
}//===============闹钟功能函数实现===================
//设置闹钟(开始闹钟)
void RTC_Alarm_Start(RTC_Alarm rtc_alarm){u8 dat;u8 alarm[4];u8 count= 0 ;//1. 设置闹钟的总开关:允许闹钟中断//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改过后,//a. 允许闹钟中断:: 把第1位置1dat |= 0x02;//b. 清除闹钟发生过的标记 :: 把 第3位 置0dat &= ~0x08; //1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);//2. 设置具体是什么时间触发闹钟。 30//2.1 时间必须是BCD格式,并且最高位第7位要置 0 //minute = DECIMAL2BCD(56) & ~0x80;if(rtc_alarm.minute_enable){alarm[0] = DECIMAL2BCD(rtc_alarm.minute) & 0x7F; //分count++;}if(rtc_alarm.hour_enable) {alarm[1] = DECIMAL2BCD(rtc_alarm.hour) & 0x7F; // 时count++;}if(rtc_alarm.day_enable){alarm[2] = DECIMAL2BCD(rtc_alarm.day) & 0x7F; // 日count++;	}if(rtc_alarm.week_enable){alarm[3] = DECIMAL2BCD(rtc_alarm.week) & 0x7F; // 星期count++;}//2.2 把分钟时间设置进去I2C_WriteNbyte(0xA2, 0x09, alarm, count);
}//停止闹钟(不设置闹钟了...)
void RTC_Alarm_Stop(){u8 dat;//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改过后,//a. 不允许闹钟中断:: 把第1位置0dat &= ~0x02;//1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);
}// 清除闹钟发生过的标记,以便闹钟在一次到点了还能触发
void RTC_Alarm_Clear_Flag(){u8 dat;//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2. 清除闹钟发生过的标记 :: 把 第3位 置0dat &= ~0x08; //1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);
}

RTC计时器设置

读取数据手册

介绍:地址为0EH的计时器寄存器,第7位置为1时代表定时器有效,为0时代表计时器无效。它的第0位和第1位表示时钟频率选择,也就是一秒数多少个数,地址为0FH的寄存器代表最终要设置多久才会执行中断,举个简单例子,如果我们设置的时钟频率为64Hz,那么我们在0FH地址中设置128则代表两秒会进行中断。

代码演示

RTC计时器设置

#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"
#include "RTC.h"
#include "Exti.h"void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_7;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_2);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void EXTI_config(){EXTI_InitTypeDef init;init.EXTI_Mode = EXT_MODE_RiseFall;			//中断模式,  	EXT_MODE_RiseFall, EXT_MODE_FallExt_Inilize( EXT_INT3 , &init);//中断使能NVIC_INT3_Init(ENABLE,Priority_1);
}// RTC时钟的中断处理: 闹钟和定时器发生中断都会调用这个函数
void RTC_ISR_Handle(){u8 dat;printf("time...\n");//来到这里之后,为了让下一次的闹钟到点了还能来这里,需要手动清理标记位//RTC_Alarm_Clear_Flag();//来到这里折后,为了让下一次的定时到点了还能来这里,需要手动清理标记位//1.1 先读取I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改之后//a.清除定时器已经触发的标记 == 0dat &= ~0x04;//1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);}void main() {u8 dat , hz , hz_count = 64 * 2;RTC_Time write_time;RTC_Alarm alarm;//0. 总开关EA = 1 ;GPIO_config();//1. RTC初始化RTC_init();//2. 串口配置UART_config();//配置外部中断EXTI_config();//写入时间write_time.year=2023;write_time.month=10;write_time.day=31;write_time.hour=13;write_time.minute=55;write_time.second=57;write_time.week=5;RTC_WriteTime(&write_time);/*//设置闹钟alarm.minute = 56;  //56分的时候响闹钟alarm.minute_enable = 1;  // 启用分钟闹钟RTC_Alarm_Start(alarm);*///=======================定时器::begin=================================//1. 设置总开关//1.1 先读取I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改之后//a. 允许定时器中断 == 1dat |= 0x01; //b. 清除定时器已经触发的标记 == 0dat &= ~0x04;//1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);//2. 设置频率(数数频率,1s钟能数多少个数)hz = HZ_64 | 0x80; //  |0x80 表示把第7位置为 0 ,也就是要启用这个频率I2C_WriteNbyte(0xA2, 0x0E, &hz, 1);//3. 设置具体数多少个数就触发中断 (其实就是过去了多久要出发中断)I2C_WriteNbyte(0xA2, 0x0F, &hz_count, 1);//=======================定时器::end=================================while(1) {//读取时间RTC_Time time;RTC_ReadTime(&time);printf("%d-%d-%d %d:%d:%d\n" , (int)time.year, (int)time.month, (int)time.day , (int)time.hour, (int)time.minute, (int)time.second );//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}

RTC计时器设置(封装)

main.c
#include "GPIO.h"
#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "I2C.h"
#include "Switch.h"
#include "RTC.h"
#include "Exti.h"void GPIO_config(void) {GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_7;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void UART_config(void) {// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<COMx_InitDefine		COMx_InitStructure;					//结构定义COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_2);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}void EXTI_config(){EXTI_InitTypeDef init;init.EXTI_Mode = EXT_MODE_RiseFall;			//中断模式,  	EXT_MODE_RiseFall, EXT_MODE_FallExt_Inilize( EXT_INT3 , &init);//中断使能NVIC_INT3_Init(ENABLE,Priority_1);
}// RTC时钟的中断处理: 闹钟和定时器发生中断都会调用这个函数
// 由于定时器和闹钟只要到点了都会触发中断,那么这个函数都会被执行,此时就需要去分辨现在是谁引发了中断,然后各走各的代码。
void RTC_ISR_Handle(){//printf("alarm...\n");printf("timer...\n");//来到这里之后,为了让下一次的闹钟到点了还能来这里,需要手动清理标记位//RTC_Alarm_Clear_Flag();//来到这里折后,为了让下一次的定时到点了还能来这里,需要手动清理标记位RTC_Timer_Clear_Flag();}void main() {RTC_Time write_time;RTC_Alarm alarm;//0. 总开关EA = 1 ;GPIO_config();//1. RTC初始化RTC_init();//2. 串口配置UART_config();//配置外部中断EXTI_config();//写入时间write_time.year=2023;write_time.month=10;write_time.day=31;write_time.hour=13;write_time.minute=55;write_time.second=57;write_time.week=5;RTC_WriteTime(&write_time);/*//设置闹钟alarm.minute = 56;  //56分的时候响闹钟alarm.minute_enable = 1;  // 启用分钟闹钟RTC_Alarm_Start(alarm);*///设置定时器RTC_Timer_Start( HZ_64, 64);while(1) {//读取时间RTC_Time time;RTC_ReadTime(&time);printf("%d-%d-%d %d:%d:%d\n" , time.year, (int)time.month, (int)time.day , (int)time.hour, (int)time.minute, (int)time.second );//每间隔1s钟读取时间delay_ms(250);delay_ms(250);delay_ms(250);delay_ms(250);}
}
RTC.h
#ifndef	__RTC_H
#define	__RTC_H#include "GPIO.h"
#include "NVIC.h"
#include "I2C.h"
#include "Switch.h"/*1. 每一个支持I2C协议元件,手册都会提供两个地址: 读地址和写地址2. 这两个地址都不是设备地址,但是他们是经过设备地址变化得来的。3. 读地址和写地址,需要用到设备地址来扮演他们的高7位,它们的第0位,设置成 0 或者是 1就可以描述现在要和这个设备做什么动作: 写的动作? 读的动作写地址: 0xA2 : 1010 0010读地址: 0xA3 : 1010 0011得到设备地址:0101 0001  ---> 0x51从设备地址得到写的地址:  0x51 << 1 ==右边补0==> 1010 0010从设备地址得到读的地址:  (0x51 << 1) |1 = 1010 0011
*///1. 宏、结构体
#define DECIMAL2BCD(i) ((( i / 10 ) << 4)  |  (i % 10))
#define BCD2DECIMAL(a , b)  (((a >> 4) & b ) * 10  +  (a & 0x0F))#define DEV_ADDRESS 0xA2  // 这里不要被迷惑了,放的应该是设备的地址 + 写的动作  =  写地址
#define MEM_ADDRESS 0x02
#define NUMBER 7//I2C 总线从地址:读,0A3H;写,0A2H
//u8 dev_addr = 0xA2 ;//表示给秒的寄存器地址进去,希望读取秒的数据
//u8 mem_addr = 0x02 ;typedef struct
{u8 second , minute , hour, day, month, week;int year ;
} RTC_Time;typedef struct
{u8  minute, hour, day,  week ;u8  minute_enable , hour_enable ,  day_enable , week_enable;
} RTC_Alarm;typedef enum
{//HZ_4096 = 0x00, HZ_64 =0x01, HZ_1 =0x02 , HZ_1_60 = 0x03//HZ_4096 = 0, HZ_64 =1, HZ_1 =2 , HZ_1_60 = 3HZ_4096, HZ_64, HZ_1 , HZ_1_60
}RTC_HZ;//2. 功能函数声明//2.1 配置、初始化
void RTC_init();//2.2 具体的功能://====================时间功能函数===============================
// 读取时间
void RTC_ReadTime(RTC_Time * time);//写入时间
void RTC_WriteTime(RTC_Time * time);//====================闹钟功能函数===============================//设置闹钟(开始闹钟)
void RTC_Alarm_Start(RTC_Alarm alarm);//停止闹钟(不设置闹钟了...)
void RTC_Alarm_Stop();// 清除闹钟发生过的标记,以便闹钟在一次到点了还能触发
void RTC_Alarm_Clear_Flag();//====================定时器功能函数===============================
//启用定时器:: 参数一:频率值,  参数二:多少个这样的hz就执行触发定时
void RTC_Timer_Start(RTC_HZ hz_value , u8 hz_count);//禁用定时器
void RTC_Timer_Stop();// 清除定时器发生过的标记,以便定时器在一次到点了还能触发
void RTC_Timer_Clear_Flag();#endif
RTC.c
#include "RTC.h"void IO_config(){GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义GPIO_InitStructure.Pin  = GPIO_Pin_2| GPIO_Pin_3;		//指定要初始化的IO,GPIO_InitStructure.Mode = GPIO_OUT_OD;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}void I2C_config(){I2C_InitTypeDef init;/*I2C总线支持的总线速度是: 100K ~ 400K400K = 24M/2/(Speed*2+4)400  = 24000 /2 / (Speed*2+4)4 = 240 / 2 / (Speed*2+4)4 = 120 / (Speed*2+4)(Speed*2+4) = 30  ===> Speed*2 = 26   ===> Speed = 13*/init.I2C_Speed = 13;				// 总线速度=Fosc/2/(Speed*2+4),      0~63init.I2C_Enable = ENABLE;				//I2C功能使能,   ENABLE, DISABLEinit.I2C_Mode = I2C_Mode_Master;					//主从模式选择,  I2C_Mode_Master,I2C_Mode_Slaveinit.I2C_MS_WDTA = DISABLE;				//主机使能自动发送,  ENABLE, DISABLE// 初始化I2C_Init(&init);//中断使能NVIC_I2C_Init(I2C_Mode_Master, DISABLE, Priority_1);//切换引脚I2C_SW(I2C_P33_P32);
}//配置、初始化
void RTC_init(){//打开外部寄存器使能EAXSFR();EA = 1;//基础配置IO_config();I2C_config();}// 读取时间
void RTC_ReadTime(RTC_Time * time){// 事先准备一个数组,让I2C把时间数据读取到这个数组里面来u8 dat[7];/* 1. 读取时钟芯片里面的时间数据2. 参数解释:2.1 参数一: 设备地址,RTC时钟芯片的地址。2.2 参数二: 具体读取的数据对应的寄存器地址2.3 参数三: 用来收取数据的地址。一般会声明一个数组出来。 一般是长度为7的数组 【年月日时分秒星期】2.4 参数四: 表示要读取几个数据*/I2C_ReadNbyte(DEV_ADDRESS, MEM_ADDRESS, dat , NUMBER);//解析数据 //获取秒//second = ((dat[0] >> 4) & 0x07 ) * 10  +  (dat[0] & 0x0F);time->second = BCD2DECIMAL(dat[0] , 0x07);//方式一: 对结构体指针的内部成员赋值//(*time).second = BCD2DECIMAL(dat[0] , 0x07);//方式二: 直接使用指针 配合 -> 对成员赋值//time->second = BCD2DECIMAL(dat[0] , 0x07);//获取分//minute = (( dat[1] >> 4 ) & 0x07 ) * 10 + (dat[1] & 0x0F);time->minute = BCD2DECIMAL(dat[1] , 0x07);//获取时//hour =  ((dat[2] >> 4 ) & 0x03) * 10   +  (dat[2] & 0x0F);time->hour = BCD2DECIMAL(dat[2] , 0x03);//获取日//day =  ((dat[3] >> 4 ) & 0x03) * 10   +  (dat[3] & 0x0F);time->day = BCD2DECIMAL(dat[3] , 0x03);//获取星期time->week = dat[4] & 0x07 ;//获取月//month = (( dat[5] >> 4 ) & 0x01 ) * 10 + (dat[5] & 0x0F);time->month = BCD2DECIMAL(dat[5] , 0x01);/*获取年1. 年份的值有 千位 +  百位 +  十位 +  个位 构成2. 但是在年的寄存器里面的只有 十位和个位的值。缺少千位和百位的值3. 需要去判定月份的第7位的值,由它来决定千位和百位的值3.1 如果一会月份的第7位 (C) 是 0 :  2000 + 年的数据   2000 ~ 20993.2 如果一会月份的第7位 (C) 是 1 :  2100 + 年的数据   2100 ~ 2199 月份:  ?111 11111000 0000-------------0000 00001000 0000*/// 得到1 或者的到 0 //C = (dat[5] & 0x80) > 0 ? 1 : 0 ;//年份的数据需要配合前面的千位和百位//year = ((dat[6] >> 4) & 0x0F) * 10   + (dat[6] & 0x0F)  ;time->year = BCD2DECIMAL(dat[6] , 0x0F);//这里是为了判定是1 还是 0 if(dat[5] & 0x80){time->year += 2100;}else{time->year += 2000;}
}//写入时间
void RTC_WriteTime(RTC_Time * time){u8 dat[7]; u8 new_year;//写入秒 (BCD格式) : 十位 +  个位dat[0]  = DECIMAL2BCD(time->second) ;//写入分 (BCD格式) : 十位 +  个位  39dat[1] = DECIMAL2BCD(time->minute) ;//写入时 (BCD格式) : 十位 +  个位dat[2] = DECIMAL2BCD(time->hour) ;//写入日  (BCD格式) : 十位 +  个位dat[3] = DECIMAL2BCD(time->day) ;//写入星期 (BCD格式) : 十位 +  个位dat[4] = DECIMAL2BCD(time->week);//写入月 (BCD格式) : 十位 +  个位//月份寄存器里面不光有月份的数据,也有世纪的数据【世纪的数据影响着后面的年份的数据】//先准备月份的数据dat[5] = DECIMAL2BCD(time->month) ;  // 0001 0010//再添加世纪的数据到月份的字节里面:修改月份的最高位 【第7位】,看C是什么? C是0,月份的第7位就是0 ,C是1 ,月份的第7位就是1//关于世纪C的赋值,要参考年份的数据,如果是20xx年,那么C就赋值 0 , 如果是 21xx年,那么C就赋值 1if(time->year >= 2100){ // 如果是 21xx年,那么C就赋值 1dat[5] |= (1 << 7);}else{ // 如果是20xx年,那么C就赋值 0 dat[5] &= ~(1 << 7);}//if(C == 0){ // C是0,月份的第7位就是0//   dat[5] &= ~(1 << 7);//}else{ // C是1 ,月份的第7位就是1//   dat[5] |= (1 << 7);//}//写入年 (BCD格式) : 十位 +  个位'// warning C182: pointer to different objects//比如年份是2023,先得到23这个值,new_year = time->year % 100;  // 23//再转换成 (BCD格式) : 十位 +  个位dat[6] = DECIMAL2BCD(new_year) ;//写数据I2C_WriteNbyte(DEV_ADDRESS,MEM_ADDRESS , dat , NUMBER);
}//===============闹钟功能函数实现===================
//设置闹钟(开始闹钟)
void RTC_Alarm_Start(RTC_Alarm rtc_alarm){u8 dat;u8 alarm[4];u8 count= 0 ;//1. 设置闹钟的总开关:允许闹钟中断//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改过后,//a. 允许闹钟中断:: 把第1位置1dat |= 0x02;//b. 清除闹钟发生过的标记 :: 把 第3位 置0dat &= ~0x08; //1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);//2. 设置具体是什么时间触发闹钟。 30//2.1 时间必须是BCD格式,并且最高位第7位要置 0 //minute = DECIMAL2BCD(56) & ~0x80;if(rtc_alarm.minute_enable){alarm[0] = DECIMAL2BCD(rtc_alarm.minute) & 0x7F; //分count++;}if(rtc_alarm.hour_enable) {alarm[1] = DECIMAL2BCD(rtc_alarm.hour) & 0x7F; // 时count++;}if(rtc_alarm.day_enable){alarm[2] = DECIMAL2BCD(rtc_alarm.day) & 0x7F; // 日count++;	}if(rtc_alarm.week_enable){alarm[3] = DECIMAL2BCD(rtc_alarm.week) & 0x7F; // 星期count++;}//2.2 把分钟时间设置进去I2C_WriteNbyte(0xA2, 0x09, alarm, count);
}//停止闹钟(不设置闹钟了...)
void RTC_Alarm_Stop(){u8 dat;//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改过后,//a. 不允许闹钟中断:: 把第1位置0dat &= ~0x02;//1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);
}// 清除闹钟发生过的标记,以便闹钟在一次到点了还能触发
void RTC_Alarm_Clear_Flag(){u8 dat;//1.1 先把原来的数据读取出来I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2. 清除闹钟发生过的标记 :: 把 第3位 置0dat &= ~0x08; //1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);
}//===============定时功能函数实现===================//启用定时器:: 参数一:频率值,  参数二:多少个这样的hz就执行触发定时
void RTC_Timer_Start(RTC_HZ hz_value , u8 hz_count){//1. 设置总开关u8 dat , hz;//1.1 先读取I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改之后//a. 允许定时器中断 == 1dat |= 0x01; //b. 清除定时器已经触发的标记 == 0dat &= ~0x04;//1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);//2. 设置频率(数数频率,1s钟能数多少个数)hz = hz_value | 0x80; //  |0x80 表示把第7位置为 0 ,也就是要启用这个频率I2C_WriteNbyte(0xA2, 0x0E, &hz, 1);//3. 设置具体数多少个数就触发中断 (其实就是过去了多久要出发中断)I2C_WriteNbyte(0xA2, 0x0F, &hz_count, 1);
}//禁用定时器
void RTC_Timer_Stop(){u8 dat;//1.1 先读取I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改之后//a. 进制定时器中断 == 0dat &= ~0x01; I2C_WriteNbyte(0xA2, 0x01, &dat, 1);
}// 清除定时器发生过的标记,以便定时器在一次到点了还能触发
void RTC_Timer_Clear_Flag(){u8 dat;//1.1 先读取I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//1.2 修改之后//a.清除定时器已经触发的标记 == 0dat &= ~0x04;//1.3 再写进去I2C_WriteNbyte(0xA2, 0x01, &dat, 1);}

RTC闹钟设置和RTC计数器设置共存

我们只需要在执行中断处进行判断,判断是闹钟进行中断还是计时器进行中断。

void RTC_ISR_Handle(){u8 dat;//1. 为了分辨到底是谁导致了中断发生,需要去读取控制寄存器I2C_ReadNbyte(0xA2, 0x01, &dat, 1);//printf("dat==%d\n" , (int)dat); // 0000 0111//2. 判定这个字节的第2位是否是 1 ,如果是1 即表示是 Timer触发了中断// dat: 							0000 0111// 鉴别第2位是否是1		0000 0100   ---- 0x04//-----------------------------------//										0000 0100 ---> 如果这个位是1,则出现的结果一定会大于 0 最终转出来的十进制不是 0//printf("dat & 0x04=%d \n" , (int)(dat & 0x04) );if(dat & 0x04){printf("timer...\n");//来到这里折后,为了让下一次的定时到点了还能来这里,需要手动清理标记位RTC_Timer_Clear_Flag();}//3. 判定这个字节的第3位是否是1 , 如果是1,即表示是Alarm触发了中断// dat: 							0000 0111// 鉴别第3位是否是1		0000 1000 --- 0x08//-----------------------------------// 										0000 0000  ---> 如果这个位是1,则出现的结果一定会大于 0 最终转出来的十进制不是 0//printf("dat & 0x08=%d \n" , (int)(dat & 0x08) );if(dat & 0x08){printf("alarm...\n");//来到这里之后,为了让下一次的闹钟到点了还能来这里,需要手动清理标记位RTC_Alarm_Clear_Flag();}}

总结

        这样RTC时钟我们就介绍完了,这里提示一下,后两个部分的RTC闹钟设置和RTC计时器设置

中的基础代码都是在上期已经封装好的代码中继续编写得到的,本期只展示了main.c中的代码,请大家特别注意!!!封装部分代码完整。下期见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/592773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opencv期末练习题(2)附带解析

图像插值与缩放 %matplotlib inline import cv2 import matplotlib.pyplot as plt def imshow(img,grayFalse,bgr_modeFalse):if gray:img cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)plt.imshow(img,cmap"gray")else:if not bgr_mode:img cv2.cvtColor(img,cv2.COLOR_B…

Selenium教程06:单选框+多选框+下拉框组件的示例练习

1.Radio单选框的示例用法&#xff0c;通过网页元素class和type属性多条件共同定位元素&#xff0c;模拟依次选中Android&#xff0c;Apple&#xff0c;Windows。 网页元素结构 <input type"radio" class"ivu-radio-input" name"ivuRadioGroup_170…

固定资产盘点系统设计与实现

固定资产盘点系统设计与实现 最近的业务需求不多&#xff0c;趁着闲的时候&#xff0c;顺便搞了个IT固定资产盘点系统&#xff0c;难度不大&#xff0c;比较实用&#xff0c;这个系统将来可以用在整个财务方面的固定资产盘点&#xff0c;结合Zebra的PDA扫描枪&#xff0c;还是比…

大模型与大模型的幻觉问题

参考 大模型中的涌现 OpenAI 科学家&#xff1a;幻觉是大模型与生俱来的特性&#xff0c;而非缺陷 大模型「幻觉」&#xff0c;看这一篇就够了&#xff5c;哈工大华为出品 大模型 什么是大模型 大语言模型&#xff08;LLM&#xff09;是基于海量文本数据训练的深度学习模型…

【数据结构】手撕排序(排序的概念及意义、直接插入和希尔排序的实现及分析)

目录 一、排序的概念及其运用 1.1排序的概念 1.2排序运用 1.3 常见的排序算法 二、插入排序 2.1基本思想&#xff1a; 2.2直接插入排序&#xff1a; 2.3步骤&#xff1a; 2.4直接插入排序的实现 三、希尔排序( 缩小增量排序 ) 3.1希尔排序的发展历史 3.2 希尔…

子进程继承了内存映射mmap导致flock文件锁泄露

背景 想要利用文件锁在进程退出时自动释放的机制&#xff0c;让守护进程管擦某些资源的释放&#xff0c;并执行相关清理/重启工作。然而发现守护进程在父进程退出时永远饿死。 问题伪代码 主进程子进程 安装信号捕获&#xff0c;每当有信号时print并退出&#xff0c;对子进…

解决跨域问题(SpringBoot)

“什么是跨域&#xff1f;” 跨域 &#xff08;Cross-Origin&#xff09; 是指在浏览器的同源策略&#xff08;Same-Origin Policy&#xff09;下&#xff0c;一个网页的源&#xff08;指协议、域名、端口号的组合&#xff09;与另一个网页的源不同。因此&#xff0c;不同源的…

提升WiFi安全的有效措施

今天买了一个USB无线网卡&#xff0c;想知道这个黑客攻击WiFi的基本流程是怎么样的&#xff0c;买回来意外就发生了&#xff0c;哈哈&#xff1a;这个网卡只支持2GWiFi网络类型&#xff0c;并且不支持WPA3网络加密方式。 你不能希望黑客永远是一个有很多钱的人&#xff0c;或许…

如何提高提问的水平?

以下是由 Google Bard 的回答。 提高提问水平&#xff0c;可以从以下几个方面入手&#xff1a; 明确问题的目的。 提问之前&#xff0c;先明确自己想通过提问达到什么目的。是想获得某个信息&#xff1f;是想了解某个概念&#xff1f;还是想解决某个问题&#xff1f;明确目的&…

综合场景搭建、在线分享,这款地理空间数据管理软件功能太多了!

《四维轻云》是一款轻量化的地理空间数据管理云平台&#xff0c;支持地理空间数据的在线管理、编辑及分享。平台具有项目管理、数据上传、场景搭建、发布分享、SDK开发等功能模块&#xff0c;支持多用户在线协作管理&#xff0c;实现了轻量化、便捷化的空间数据应用。 一、发布…

YOLOv5改进Shape-IoU损失函数:元旦假期最新 IoU论文 | 考虑边界框形状和比例的更准确的指标,YOLO性能提升

💡本篇内容:YOLOv5改进Shape-IoU损失函数:元旦假期最新 IoU论文 | 考虑边界框形状和比例的更准确的指标,YOLO性能提升 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv5 按步骤操作运行改进后的代码即可 💡论文地址:https://arxiv.org/abs/2312.17663 2023年12月…

linux下超级程序!在linux界面实现类图像化界面的操作体验!

linux下超级程序&#xff01;在linux界面实现类图像化界面的操作体验&#xff01; 本期带来一个超级程序&#xff01;在linux界面实现类图像化界面的操作体验。具体功能代码如下: 1500行完整代码想要完成部署&#xff0c;只需在本地创建一个LinuxGJ.sh的文件&#xff0c;然后…

vue+ts element-plu是页码器根据屏幕宽度变化,解决刷新后初始化值问题

实现思路&#xff1a;组件挂载后执行初始化操作&#xff0c;初始化添加事件监听器&#xff0c;当浏览器窗口大小发生变化时会调用这个函数handleResize <el-pagination v-model:current-page"currentPage" background :total"total" layout"prev,…

Go中interface != nil不一定不是nil

摘要&#xff1a; interface{} 值 ! nil不一定不是nil&#xff0c;应使用reflect库判断是否是nil。 测试示例&#xff1a; // todo interface ! nil 不一定 不是nil var value map[string]interface{} reqMap : make(map[string]interface{}) reqMap["key"] valu…

Vue.js 3.4版本发布:解析速度提升2倍,双向绑定革新等新功能

引言 随着2024年的来临,Vue团队的领军人物Evan You宣布了Vue.js 3.4的发布。这个版本不仅仅是修复了一些bug,还带来了一些非常实用的新功能和性能提升。 解析速度提升2倍 这次更新中,Vue.js 3.4实现了解析速度的大幅提升。尤其是在构建模板和脚本的源代码映射时,单文件组…

优维科技2024战略定位:新一代运维核心系统提供商

01 经济复苏「走远路」 过去几年&#xff0c;全球经济持续低迷&#xff0c;2024会迎来转机吗&#xff1f; 回顾2023年&#xff0c;尽管经济复苏动能式微&#xff0c;但全球经济因有效控制通胀而展现出来的韧性&#xff0c;让包括中国在内的大部分经济体躲过了深度衰退的陷阱&…

C语言所有操作符总结

目录 算术操作符&#xff1a; 移位操作符&#xff1a; 位操作符&#xff1a; 赋值操作符&#xff1a; 单目操作符&#xff1a; 关系操作符&#xff1a; 逻辑操作符&#xff1a; 以及特殊的操作符&#xff08;条件&#xff0c;逗号&#xff0c;下标&#xff0c;调用&…

鸿蒙开发第一天

一、开发准备工作 1、开发工具的安装 1&#xff09;下载地址&#xff1a;https://developer.huawei.com/consumer/cn/deveco-studio/ 2&#xff09;查询API文档链接&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/syscap-00000014080893…

Spring之bean的实例化方式

1.使用构造方法实例化bean&#xff08;利用反射&#xff09; import lombok.Data;Data public class People {private String name;private Integer age;private String eat; }<?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http:/…

【MySQL】关于日期转换的方法

力扣题 1、题目地址 1853. 转换日期格式 2、模拟表 表: Days Column NameTypedaydate day 是这个表的主键。 3、要求 给定一个Days表&#xff0c;请你编写SQL查询语句&#xff0c;将Days表中的每一个日期转化为"day_name, month_name day, year"格式的字符串…