stm32f1系列的单片机本身自带的rtc实时时钟外设只是一个单纯的32位计数器,没有分立为年月日、小时、分钟、秒等寄存器,使用起来不是很方便。这时可以考虑使用外部rtc芯片,比如使用spi接口的双向单数据线方式的ds1302实时时钟芯片,或是fsmc接口的带内部电池的ds12885、ds12887、ds12887a、ds12c887和ds12c887a等芯片。
【接线】
ds12c887的vcc接+5v,gnd接地。mot悬空,ad0~1接pd14~15,ad2~3接pd0~1,ad4~7接pe7~10。reset引脚接pa8(这个可以随便接,与fsmc无关),ds接pd4,r/w接pd5,cs接pd7(这是100脚的单片机上唯一的fsmc片选引脚)。
pb7(nadv)必须通过一个反相器后才能接到as,并且不可以用地址线a8~a25代替(ds12c887时序要求as拉低后ad0~ad7上的地址信号才能撤销,不可同时撤销。如果as接到ad8上,则地址信号ad8~ad0会被同时撤销,不符合时序要求)。其接法如下图所示:
特别注意74hc04的电源接的是3.3v,而ds12c887的电源接的是5v。
最好不要用一个三极管来代替74hc04反相器,因为三极管的切换速度太慢了,而且搞得不好功耗也会比74hc04高。例如,使用9012型的三极管,发射极接3.3v,基极通过一个10kω的电阻接pb7,集电极接pb12后再通过一个10kω的电阻接gnd,运行下面的程序:
void test(void){ uint8_t i; for (i = 0; i arr = 10 * nms - 1; tim6->psc = 7199; tim6->egr = tim_egr_ug; tim6->cr1 = tim_cr1_opm | tim_cr1_cen; while (tim6->cr1 & tim_cr1_cen);}int fputc(int ch, file *fp){ if (fp == stdout) { if (ch == '\n') { while ((usart1->sr & usart_sr_txe) == 0); usart1->dr = '\r'; } while ((usart1->sr & usart_sr_txe) == 0); usart1->dr = ch; } return ch;}int main(void){ char buf[20]; rcc->ahbenr |= rcc_ahbenr_fsmcen; rcc->apb1enr = rcc_apb1enr_tim6en; rcc->apb2enr = rcc_apb2enr_iopaen | rcc_apb2enr_iopben | rcc_apb2enr_iopden | rcc_apb2enr_iopeen | rcc_apb2enr_usart1en; gpioa->crh = 0x444444b3; // pa8为rst复位引脚(默认输出低电平), pa9为串口1发送引脚 gpiob->crl = 0xb4444444; // pb7为nadv, 取反后送到as引脚上, 该引脚不可用地址线代替! gpiod->crl = 0xb4bb44bb; // pd0~1为ad2~3, pd4为noe接ds引脚, pd5为nwe接rw引脚, pd7为ne1片选引脚接cs gpiod->crh = 0xbb444444; // pd14~15为ad0~1 gpioe->crl = 0xb4444444; // pe7为ad4 gpioe->crh = 0x44444bbb; // pe8~10为ad5~7 usart1->brr = 625; // 串口波特率为115200 usart1->cr1 = usart_cr1_ue | usart_cr1_te; // 串口1只允许发送 // fsmc的bank1, subbank1设为8位nor flash地址/数据线复用模式, 关闭nwait引脚 fsmc_bank1->btcr[0] &= ~(fsmc_bcr1_waiten | fsmc_bcr1_mwid); // 下面为可选配置, 用于加快访存速度 // hclk=72mhz时, datast的最小值为2, 即3xhclk clock cycles fsmc_bank1->btcr[1] = (fsmc_bank1->btcr[1] & ~(fsmc_btr1_busturn | fsmc_btr1_datast | fsmc_btr1_addhld | fsmc_btr1_addset)) | fsmc_btr1_datast_1 | fsmc_btr1_addhld_0; printf(stm32f103ve fsmc ds12c887\n); delay(200); gpioa->bsrr = gpio_bsrr_bs8; // reset=1, 撤销复位信号 // 读写自由sram区域 strcpy((char *)0x60000033, this is a string!); memcpy(buf, (char *)0x60000033, sizeof(buf)); printf(str=%s\n, buf); // 读a~d寄存器 printf(a=0x%02x b=0x%02x c=0x%02x d=0x%02x\n, *(__io uint8_t *)0x6000000a, *(__io uint8_t *)0x6000000b, *(__io uint8_t *)0x6000000c, *(__io uint8_t *)0x6000000d); while (1) __wfi();}void hardfault_handler(void){ printf(hard error!\n); while (1);}【程序1运行结果】
stm32f103ve fsmc ds12c887str=this is a string!a=0x00 b=0x82 c=0x00 d=0x00
d寄存器的最高位为0,看来ds12c887芯片里面的电池早就没电了。。。。。
地址0x33~0x7f这一区域为自由sram,可以任意读写,不影响芯片功能。
以下为连线的实物图。我用的是带8mhz晶振的微雪stm32f103vet6核心板做的实验。
左边的芯片是ds12c887,右边那个小的芯片是74hc04反相器。
【程序2(库函数版)】
#include #include #include typedef __packed struct{ __io uint8_t sec; __io uint8_t secalr; __io uint8_t min; __io uint8_t minalr; __io uint8_t hour; __io uint8_t houralr; __io uint8_t day; __io uint8_t date; __io uint8_t month; __io uint8_t year; __io uint8_t cr1; __io uint8_t cr2; __io uint8_t cr3; __io uint8_t cr4; __io uint8_t ram1[36]; // 0x0e-0x31 __io uint8_t century; __io uint8_t ram2[77]; // 0x33-0x7f} ds12c887_typedef;#define rtc2 ((ds12c887_typedef *)0x60000000)// 延时n毫秒void delay(uint16_t nms){ tim_timebaseinittypedef tim; tim_timebasestructinit(&tim); tim.tim_period = 10 * nms - 1; tim.tim_prescaler = 7199; tim_timebaseinit(tim6, &tim); tim_clearflag(tim6, tim_flag_update); tim_selectonepulsemode(tim6, tim_opmode_single); tim_cmd(tim6, enable); while (tim_getflagstatus(tim6, tim_flag_update) == reset);}int fputc(int ch, file *fp){ if (fp == stdout) { if (ch == '\n') { while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, '\r'); } while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, ch); } return ch;}int main(void){ fsmc_norsraminittypedef fsmc; fsmc_norsramtiminginittypedef fsmc_timing; gpio_inittypedef gpio; usart_inittypedef usart; rcc_ahbperiphclockcmd(rcc_ahbperiph_fsmc, enable); rcc_apb1periphclockcmd(rcc_apb1periph_tim6, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_gpiob | rcc_apb2periph_gpiod | rcc_apb2periph_gpioe | rcc_apb2periph_usart1, enable); // pa8为rst复位引脚(默认输出低电平) gpio.gpio_mode = gpio_mode_out_pp; gpio.gpio_pin = gpio_pin_8; gpio.gpio_speed = gpio_speed_2mhz; gpio_init(gpioa, &gpio); // pa9为串口1发送引脚 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_9; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpioa, &gpio); // pb7为nadv, 取反后送到as引脚上, 该引脚不可用地址线代替 gpio.gpio_pin = gpio_pin_7; gpio_init(gpiob, &gpio); // pd0~1为ad2~3, pd4为noe接ds引脚, pd5为nwe接rw引脚, pd7为ne1片选引脚接cs, pd14~15为ad0~1 gpio.gpio_pin = gpio_pin_0 | gpio_pin_1 | gpio_pin_4 | gpio_pin_5 | gpio_pin_7 | gpio_pin_14 | gpio_pin_15; gpio_init(gpiod, &gpio); // pe7~10为ad4~7 gpio.gpio_pin = gpio_pin_7 | gpio_pin_8 | gpio_pin_9 | gpio_pin_10; gpio_init(gpioe, &gpio); // 初始化串口1 usart_structinit(&usart); usart.usart_baudrate = 115200; usart.usart_mode = usart_mode_tx; usart_init(usart1, &usart); usart_cmd(usart1, enable); // fsmc的bank1, subbank1设为8位nor flash地址/数据线复用模式, 关闭nwait引脚 fsmc.fsmc_readwritetimingstruct = &fsmc_timing; fsmc.fsmc_writetimingstruct = &fsmc_timing; fsmc_norsramstructinit(&fsmc); fsmc.fsmc_memorytype = fsmc_memorytype_nor; // 存储器类型为nor flash fsmc.fsmc_waitsignal = fsmc_waitsignal_disable; // 不使用nwait引脚 fsmc_timing.fsmc_addressholdtime = 1; fsmc_timing.fsmc_addresssetuptime = 0; fsmc_timing.fsmc_busturnaroundduration = 0; fsmc_timing.fsmc_datasetuptime = 2; // hclk=72mhz时, datast的最小值为2, 即3xhclk clock cycles fsmc_norsraminit(&fsmc); fsmc_norsramcmd(fsmc_bank1_norsram1, enable); // 虽然subbank1默认是启用的, 但执行fsmc_norsraminit函数时会被关闭, 所以需要再次开启 printf(stm32f103ve fsmc ds12c887\n); delay(200); gpio_writebit(gpioa, gpio_pin_8, bit_set); // reset=1, 撤销复位信号 // 读写自由sram区域 printf(general-purpose ram1 addr: 0x%08x\n, (uint32_t)rtc2->ram1); printf(general-purpose ram2 addr: 0x%08x\n, (uint32_t)rtc2->ram2); strcpy((char *)rtc2->ram1, flexible static memory controller); strcpy((char *)rtc2->ram2, muxed mode - multiplexed asynchronous access to nor flash memory); printf(str1=%s\n, rtc2->ram1); printf(str2=%s\n, rtc2->ram2); // 读a~d寄存器 printf(a=0x%02x b=0x%02x c=0x%02x d=0x%02x\n, rtc2->cr1, rtc2->cr2, rtc2->cr3, rtc2->cr4); while (1) __wfi();}void hardfault_handler(void){ printf(hard error!\n); while (1);}【程序2运行结果】
stm32f103ve fsmc ds12c887general-purpose ram1 addr: 0x6000000egeneral-purpose ram2 addr: 0x60000033str1=flexible static memory controllerstr2=muxed mode - multiplexed asynchronous access to nor flash memorya=0x00 b=0x82 c=0x00 d=0x00
【程序3:实际走时测试】
#include #include #include ds12c887.h// 延时n毫秒void delay(uint16_t nms){ tim_timebaseinittypedef tim; tim_timebasestructinit(&tim); tim.tim_period = 10 * nms - 1; tim.tim_prescaler = 7199; tim_timebaseinit(tim6, &tim); tim_clearflag(tim6, tim_flag_update); tim_selectonepulsemode(tim6, tim_opmode_single); tim_cmd(tim6, enable); while (tim_getflagstatus(tim6, tim_flag_update) == reset);}int fputc(int ch, file *fp){ if (fp == stdout) { if (ch == '\n') { while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, '\r'); } while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, ch); } return ch;}void ds12c887_init(void){ fsmc_norsraminittypedef fsmc; fsmc_norsramtiminginittypedef fsmc_timing; gpio_inittypedef gpio; rcc_ahbperiphclockcmd(rcc_ahbperiph_fsmc, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_gpiob | rcc_apb2periph_gpiod | rcc_apb2periph_gpioe, enable); // pa8为rst复位引脚(默认输出低电平) gpio.gpio_mode = gpio_mode_out_pp; gpio.gpio_pin = gpio_pin_8; gpio.gpio_speed = gpio_speed_2mhz; gpio_init(gpioa, &gpio); // pb7为nadv, 取反后送到as引脚上, 该引脚不可用地址线代替 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_7; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpiob, &gpio); // pd0~1为ad2~3, pd4为noe接ds引脚, pd5为nwe接rw引脚, pd7为ne1片选引脚接cs, pd14~15为ad0~1 gpio.gpio_pin = gpio_pin_0 | gpio_pin_1 | gpio_pin_4 | gpio_pin_5 | gpio_pin_7 | gpio_pin_14 | gpio_pin_15; gpio_init(gpiod, &gpio); // pe7~10为ad4~7 gpio.gpio_pin = gpio_pin_7 | gpio_pin_8 | gpio_pin_9 | gpio_pin_10; gpio_init(gpioe, &gpio); // fsmc的bank1, subbank1设为8位nor flash地址/数据线复用模式, 关闭nwait引脚 fsmc.fsmc_readwritetimingstruct = &fsmc_timing; fsmc.fsmc_writetimingstruct = &fsmc_timing; fsmc_norsramstructinit(&fsmc); fsmc.fsmc_memorytype = fsmc_memorytype_nor; // 存储器类型为nor flash fsmc.fsmc_waitsignal = fsmc_waitsignal_disable; // 不使用nwait引脚 fsmc_timing.fsmc_addressholdtime = 1; fsmc_timing.fsmc_addresssetuptime = 0; fsmc_timing.fsmc_busturnaroundduration = 0; fsmc_timing.fsmc_datasetuptime = 2; // hclk=72mhz时, datast的最小值为2, 即3xhclk clock cycles fsmc_norsraminit(&fsmc); fsmc_norsramcmd(fsmc_bank1_norsram1, enable); // 虽然subbank1默认是启用的, 但执行fsmc_norsraminit函数时会被关闭, 所以需要再次开启 delay(200); gpio_writebit(gpioa, gpio_pin_8, bit_set); // reset=1, 撤销复位信号}int main(void){ gpio_inittypedef gpio; usart_inittypedef usart; rcc_apb1periphclockcmd(rcc_apb1periph_tim6, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_usart1, enable); // pa9为串口1发送引脚 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_9; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpioa, &gpio); // 初始化串口1 usart_structinit(&usart); usart.usart_baudrate = 115200; usart.usart_mode = usart_mode_tx; usart_init(usart1, &usart); usart_cmd(usart1, enable); ds12c887_init(); printf(ds12c887 rtc\n); // 写入时间 if (rtc2->cr2 & rtc2_cr2_set) { rtc2->cr2 |= rtc2_cr2_dm | rtc2_cr2_24_12; rtc2->century = 20; rtc2->year = 17; rtc2->month = 9; rtc2->date = 27; rtc2->hour = 21; rtc2->min = 6; rtc2->sec = 30; rtc2->day = 3; // 星期必须手动写入, 无法自动计算 rtc2->cr1 = rtc2_cr1_dv1; // 打开晶振 rtc2->cr2 &= ~rtc2_cr2_set; // 时钟开始走时 } // 第一次启动后需要等较长的时间时钟才能开始走时 while (1) { printf(%02d%02d-%02d-%02d, rtc2->century, rtc2->year, rtc2->month, rtc2->date); if ((rtc2->cr4 & rtc2_cr4_vrt) == 0) printf(!); // 如果电池已耗尽, 则显示感叹号
printf( %02d:%02d:%02d , rtc2->hour, rtc2->min, rtc2->sec); printf(%c, ?mtwtfss[rtc2->day]); // 星期的第一个字母 printf(%c, ?ouehrau[rtc2->day]); // 星期的第二个字母 printf(%c\n, ?neduitn[rtc2->day]); // 星期的第三个字母
// 等待时间更新完毕 while ((rtc2->cr1 & rtc2_cr1_uip) == 0); while (rtc2->cr1 & rtc2_cr1_uip); }}void hardfault_handler(void){ printf(hard error!\n); while (1);}
【头文件ds12c887.h】
typedef __packed struct{ __io uint8_t sec; __io uint8_t secalr; __io uint8_t min; __io uint8_t minalr; __io uint8_t hour; __io uint8_t houralr; __io uint8_t day; __io uint8_t date; __io uint8_t month; __io uint8_t year; __io uint8_t cr1; __io uint8_t cr2; __io uint8_t cr3; __io uint8_t cr4; __io uint8_t ram1[36]; // 0x0e-0x31 __io uint8_t century; __io uint8_t ram2[77]; // 0x33-0x7f} ds12c887_typedef;#define rtc2 ((ds12c887_typedef *)0x60000000)#define rtc2_cr1_uip 0x80#define rtc2_cr1_dv2 0x40#define rtc2_cr1_dv1 0x20#define rtc2_cr1_dv0 0x10#define rtc2_cr1_rs3 0x08#define rtc2_cr1_rs2 0x04#define rtc2_cr1_rs1 0x02#define rtc2_cr1_rs0 0x01#define rtc2_cr2_set 0x80#define rtc2_cr2_pie 0x40#define rtc2_cr2_aie 0x20#define rtc2_cr2_uie 0x10#define rtc2_cr2_sqwe 0x08#define rtc2_cr2_dm 0x04#define rtc2_cr2_24_12 0x02#define rtc2_cr2_dse 0x01#define rtc2_cr3_irqf 0x80#define rtc2_cr3_pf 0x40#define rtc2_cr3_af 0x20#define rtc2_cr3_uf 0x10#define rtc2_cr4_vrt 0x80void ds12c887_init(void);
【程序3~5的运行结果】
ds12c887 rtc2017-09-27! 21:06:41 wed2017-09-27! 21:06:42 wed2017-09-27! 21:06:43 wed2017-09-27! 21:06:44 wed2017-09-27! 21:06:45 wed2017-09-27! 21:06:46 wed2017-09-27! 21:06:47 wed2017-09-27! 21:06:48 wed2017-09-27! 21:06:49 wed2017-09-27! 21:06:50 wed2017-09-27! 21:06:51 wed2017-09-27! 21:06:52 wed2017-09-27! 21:06:53 wed2017-09-27! 21:06:54 wed2017-09-27! 21:06:55 wed2017-09-27! 21:06:56 wed2017-09-27! 21:06:57 wed2017-09-27! 21:06:58 wed2017-09-27! 21:06:59 wed2017-09-27! 21:07:00 wed2017-09-27! 21:07:01 wed2017-09-27! 21:07:02 wed2017-09-27! 21:07:03 wed2017-09-27! 21:07:04 wed2017-09-27! 21:07:05 wed
【程序4:利用ds12c887的中断输出引脚irq唤醒处于stop模式的stm32单片机,并输出当前时间】
中断引脚irq接到单片机的pb1引脚上。该程序的运行结果和上面的程序相同。
#include #include #include ds12c887.h// 延时n毫秒void delay(uint16_t nms){ tim_timebaseinittypedef tim; tim_timebasestructinit(&tim); tim.tim_period = 10 * nms - 1; tim.tim_prescaler = 7199; tim_timebaseinit(tim6, &tim); tim_clearflag(tim6, tim_flag_update); tim_selectonepulsemode(tim6, tim_opmode_single); tim_cmd(tim6, enable); while (tim_getflagstatus(tim6, tim_flag_update) == reset);}int fputc(int ch, file *fp){ if (fp == stdout) { if (ch == '\n') { while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, '\r'); } while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, ch); } return ch;}void ds12c887_init(void){ fsmc_norsraminittypedef fsmc; fsmc_norsramtiminginittypedef fsmc_timing; gpio_inittypedef gpio; rcc_ahbperiphclockcmd(rcc_ahbperiph_fsmc, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_gpiob | rcc_apb2periph_gpiod | rcc_apb2periph_gpioe, enable); // pa8为rst复位引脚(默认输出低电平) gpio.gpio_mode = gpio_mode_out_pp; gpio.gpio_pin = gpio_pin_8; gpio.gpio_speed = gpio_speed_2mhz; gpio_init(gpioa, &gpio); // pb1为中断引脚 gpio.gpio_mode = gpio_mode_ipu; // 必须要设为带上拉电阻的输入 gpio.gpio_pin = gpio_pin_1; gpio_init(gpiob, &gpio); // pb7为nadv, 取反后送到as引脚上, 该引脚不可用地址线代替 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_7; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpiob, &gpio); // pd0~1为ad2~3, pd4为noe接ds引脚, pd5为nwe接rw引脚, pd7为ne1片选引脚接cs, pd14~15为ad0~1 gpio.gpio_pin = gpio_pin_0 | gpio_pin_1 | gpio_pin_4 | gpio_pin_5 | gpio_pin_7 | gpio_pin_14 | gpio_pin_15; gpio_init(gpiod, &gpio); // pe7~10为ad4~7 gpio.gpio_pin = gpio_pin_7 | gpio_pin_8 | gpio_pin_9 | gpio_pin_10; gpio_init(gpioe, &gpio); // fsmc的bank1, subbank1设为8位nor flash地址/数据线复用模式, 关闭nwait引脚 fsmc.fsmc_readwritetimingstruct = &fsmc_timing; fsmc.fsmc_writetimingstruct = &fsmc_timing; fsmc_norsramstructinit(&fsmc); fsmc.fsmc_memorytype = fsmc_memorytype_nor; // 存储器类型为nor flash fsmc.fsmc_waitsignal = fsmc_waitsignal_disable; // 不使用nwait引脚 fsmc_timing.fsmc_addressholdtime = 1; fsmc_timing.fsmc_addresssetuptime = 0; fsmc_timing.fsmc_busturnaroundduration = 0; fsmc_timing.fsmc_datasetuptime = 2; // hclk=72mhz时, datast的最小值为2, 即3xhclk clock cycles fsmc_norsraminit(&fsmc); fsmc_norsramcmd(fsmc_bank1_norsram1, enable); // 虽然subbank1默认是启用的, 但执行fsmc_norsraminit函数时会被关闭, 所以需要再次开启 delay(200); gpio_writebit(gpioa, gpio_pin_8, bit_set); // reset=1, 撤销复位信号}void ds12c887_enableit(void){ exti_inittypedef exti; rcc_apb2periphclockcmd(rcc_apb2periph_afio, enable); gpio_extilineconfig(gpio_portsourcegpiob, gpio_pinsource1); exti.exti_line = exti_line1; exti.exti_linecmd = enable; exti.exti_mode = exti_mode_interrupt; exti.exti_trigger = exti_trigger_falling; exti_init(&exti); nvic_enableirq(exti1_irqn);}// 根据当前时钟情况设置串口1的波特率寄存器void init_usart1(void){ usart_inittypedef usart; usart_structinit(&usart); usart.usart_baudrate = 115200; usart.usart_mode = usart_mode_tx; usart_init(usart1, &usart);}int main(void){ gpio_inittypedef gpio; rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_tim6, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_usart1, enable); // pa9为串口1发送引脚 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_9; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpioa, &gpio); init_usart1(); usart_cmd(usart1, enable); ds12c887_init(); ds12c887_enableit(); printf(ds12c887 rtc\n); // 写入时间 if (rtc2->cr2 & rtc2_cr2_set) { rtc2->cr2 |= rtc2_cr2_dm | rtc2_cr2_24_12; rtc2->century = 20; rtc2->year = 17; rtc2->month = 9; rtc2->date = 27; rtc2->hour = 21; rtc2->min = 6; rtc2->sec = 30; rtc2->day = 3; // 星期必须手动写入, 无法自动计算 rtc2->cr1 = rtc2_cr1_dv1; // 打开晶振 rtc2->cr2 &= ~rtc2_cr2_set; // 时钟开始走时 } rtc2->cr2 |= rtc2_cr2_uie; // 开时钟更新中断 nvic_systemlpconfig(nvic_lp_sleeponexit, enable); // 进入中断并退出中断后, 立即进入低功耗模式 while (usart_getflagstatus(usart1, usart_flag_tc) == reset); // 等待串口字符发送完毕 pwr_enterstopmode(pwr_regulator_lowpower, pwr_stopentry_wfi); // 进入stop模式, 等待外部中断到来 while (1); // 这里面的内容永远也不会执行}// 第一次启动后需要等较长的时间时钟才能开始走时void exti1_irqhandler(void){ exti_clearflag(exti_line1); // 清除stm32的外部中断标志位 rtc2->cr3; // 读c寄存器可清除中断标志位 init_usart1(); // 从stop模式唤醒后, 外部高速晶振已停振, 需重新设置波特率寄存器 printf(%02d%02d-%02d-%02d, rtc2->century, rtc2->year, rtc2->month, rtc2->date); if ((rtc2->cr4 & rtc2_cr4_vrt) == 0) printf(!); // 如果电池已耗尽, 则显示感叹号
printf( %02d:%02d:%02d , rtc2->hour, rtc2->min, rtc2->sec); printf(%c, ?mtwtfss[rtc2->day]); // 星期的第一个字母 printf(%c, ?ouehrau[rtc2->day]); // 星期的第二个字母 printf(%c\n, ?neduitn[rtc2->day]); // 星期的第三个字母 while (usart_getflagstatus(usart1, usart_flag_tc) == reset); // 进入stop模式前先等待串口字符发送完毕}void hardfault_handler(void){ printf(hard error!\n); while (1);}
【程序5:外部事件唤醒stm32单片机并输出当前时间】
ds12c887的中断引脚连接到exti_line1上,但配置为事件方式(event)而不是中断方式(interrupt)唤醒stm32单片机。该程序的运行结果和上面的程序相同。
#include #include #include ds12c887.h// 延时n毫秒void delay(uint16_t nms){ tim_timebaseinittypedef tim; tim_timebasestructinit(&tim); tim.tim_period = 10 * nms - 1; tim.tim_prescaler = 7199; tim_updaterequestconfig(tim6, tim_updatesource_regular); tim_timebaseinit(tim6, &tim); tim_selectonepulsemode(tim6, tim_opmode_single); tim_cmd(tim6, enable); // 进入睡眠模式, 等待定时器唤醒 tim_itconfig(tim6, tim_it_update, enable); nvic_enableirq(tim6_irqn); __wfi();}int fputc(int ch, file *fp){ if (fp == stdout) { if (ch == '\n') { while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, '\r'); } while (usart_getflagstatus(usart1, usart_flag_txe) == reset); usart_senddata(usart1, ch); } return ch;}void ds12c887_init(void){ fsmc_norsraminittypedef fsmc; fsmc_norsramtiminginittypedef fsmc_timing; gpio_inittypedef gpio; rcc_ahbperiphclockcmd(rcc_ahbperiph_fsmc, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_gpiob | rcc_apb2periph_gpiod | rcc_apb2periph_gpioe, enable); // pa8为rst复位引脚(默认输出低电平) gpio.gpio_mode = gpio_mode_out_pp; gpio.gpio_pin = gpio_pin_8; gpio.gpio_speed = gpio_speed_2mhz; gpio_init(gpioa, &gpio); // pb1为中断引脚 gpio.gpio_mode = gpio_mode_ipu; // 必须要设为带上拉电阻的输入 gpio.gpio_pin = gpio_pin_1; gpio_init(gpiob, &gpio); // pb7为nadv, 取反后送到as引脚上, 该引脚不可用地址线代替 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_7; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpiob, &gpio); // pd0~1为ad2~3, pd4为noe接ds引脚, pd5为nwe接rw引脚, pd7为ne1片选引脚接cs, pd14~15为ad0~1 gpio.gpio_pin = gpio_pin_0 | gpio_pin_1 | gpio_pin_4 | gpio_pin_5 | gpio_pin_7 | gpio_pin_14 | gpio_pin_15; gpio_init(gpiod, &gpio); // pe7~10为ad4~7 gpio.gpio_pin = gpio_pin_7 | gpio_pin_8 | gpio_pin_9 | gpio_pin_10; gpio_init(gpioe, &gpio); // fsmc的bank1, subbank1设为8位nor flash地址/数据线复用模式, 关闭nwait引脚 fsmc.fsmc_readwritetimingstruct = &fsmc_timing; fsmc.fsmc_writetimingstruct = &fsmc_timing; fsmc_norsramstructinit(&fsmc); fsmc.fsmc_memorytype = fsmc_memorytype_nor; // 存储器类型为nor flash fsmc.fsmc_waitsignal = fsmc_waitsignal_disable; // 不使用nwait引脚 fsmc_timing.fsmc_addressholdtime = 1; fsmc_timing.fsmc_addresssetuptime = 0; fsmc_timing.fsmc_busturnaroundduration = 0; fsmc_timing.fsmc_datasetuptime = 2; // hclk=72mhz时, datast的最小值为2, 即3xhclk clock cycles fsmc_norsraminit(&fsmc); fsmc_norsramcmd(fsmc_bank1_norsram1, enable); // 虽然subbank1默认是启用的, 但执行fsmc_norsraminit函数时会被关闭, 所以需要再次开启 delay(200); gpio_writebit(gpioa, gpio_pin_8, bit_set); // reset=1, 撤销复位信号}void ds12c887_enableit(void){ exti_inittypedef exti; rcc_apb2periphclockcmd(rcc_apb2periph_afio, enable); gpio_extilineconfig(gpio_portsourcegpiob, gpio_pinsource1); exti.exti_line = exti_line1; exti.exti_linecmd = enable; exti.exti_mode = exti_mode_event; // event模式 exti.exti_trigger = exti_trigger_falling; exti_init(&exti);}int main(void){ gpio_inittypedef gpio; usart_inittypedef usart; rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_tim6, enable); rcc_apb2periphclockcmd(rcc_apb2periph_gpioa | rcc_apb2periph_usart1, enable); // pa9为串口1发送引脚 gpio.gpio_mode = gpio_mode_af_pp; gpio.gpio_pin = gpio_pin_9; gpio.gpio_speed = gpio_speed_50mhz; gpio_init(gpioa, &gpio); // 初始化并启用串口1 usart_structinit(&usart); usart.usart_baudrate = 115200; usart.usart_mode = usart_mode_tx; usart_init(usart1, &usart); usart_cmd(usart1, enable); ds12c887_init(); ds12c887_enableit(); printf(ds12c887 rtc\n); // 写入时间 if (rtc2->cr2 & rtc2_cr2_set) { rtc2->cr2 |= rtc2_cr2_dm | rtc2_cr2_24_12; rtc2->century = 20; rtc2->year = 17; rtc2->month = 9; rtc2->date = 27; rtc2->hour = 21; rtc2->min = 6; rtc2->sec = 30; rtc2->day = 3; // 星期必须手动写入, 无法自动计算 rtc2->cr1 = rtc2_cr1_dv1; // 打开晶振 rtc2->cr2 &= ~rtc2_cr2_set; // 时钟开始走时 } // 第一次启动后需要等较长的时间时钟才能开始走时 rtc2->cr2 |= rtc2_cr2_uie; // 开时钟更新中断 while (1) { while (usart_getflagstatus(usart1, usart_flag_tc) == reset); // 等待串口字符发送完毕 pwr_enterstopmode(pwr_regulator_lowpower, pwr_stopentry_wfe); // 进入stop模式, 等待外部中断到来
// 唤醒后, 输出当前时间 rtc2->cr3; // 读c寄存器可清除中断标志位
usart_init(usart1, &usart); // 从stop模式唤醒后, 外部高速晶振已停振, 需重新设置波特率寄存器 printf(%02d%02d-%02d-%02d, rtc2->century, rtc2->year, rtc2->month, rtc2->date); if ((rtc2->cr4 & rtc2_cr4_vrt) == 0) printf(!); // 如果电池已耗尽, 则显示感叹号
printf( %02d:%02d:%02d , rtc2->hour, rtc2->min, rtc2->sec); printf(%c, ?mtwtfss[rtc2->day]); // 星期的第一个字母 printf(%c, ?ouehrau[rtc2->day]); // 星期的第二个字母 printf(%c\n, ?neduitn[rtc2->day]); // 星期的第三个字母 }}void hardfault_handler(void){ printf(hard error!\n); while (1);}void tim6_irqhandler(void){ tim_clearitpendingbit(tim6, tim_it_update);}
PLC系统中屏蔽层单端接地和屏蔽层双端接地
坚硬超乎你的想象:LG gram笔记本暴力测试
5G技术支持下将全面提升VR/AR的体验
关于智能手机将步PC的介绍和应用
CA3140(IC1)用作电阻/电压变换器
100脚的STM32F103VE单片机通过FSMC接口读写DS12C887时钟芯片中的寄存器
独立后的荣耀能否最终赢下这一局?
中国通用航空业现处在初创期,未来发展空间巨大
研华科技推出基于Arm® Cortex®-A35 NXP i.MX 8X应用处理器
我国工业互联网呈现蓬勃发展的态势
不得不关注的六个LED照明技术细节
全国首条智慧地下电缆线路的监控信息正式接入
助力低碳,深耕低温锡膏技术引领行业工艺发展
有哪些平价的蓝牙耳机?四款适合学生的平价蓝牙耳机推荐
冠层分析仪的作用是什么,它有哪些应用
传感器前级信号处理
UWB室内定位高精度定位应用的宠儿
ADI新数字隔离器封装确保医疗和工业应用安全
基于A2DP框架的近距离无线音频通信研究
联发科公布第三季财报 营收达670亿元并表示对AI的投入已有相当的成果