100脚的STM32F103VE单片机通过FSMC接口读写DS12C887时钟芯片中的寄存器

100脚的STM32F103VE单片机通过FSMC接口读写DS12C887时钟芯片中的寄存器,第1张

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 < 3; i++) { printf("PB7=0, PB12=%d\n", GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)); GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_SET); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); printf("PB7=1, PB12=%d\n", GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)); GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_RESET); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); } }

PB7为GPIO_Mode_Out_PP,PB12为GPIO_Mode_IN_FLOATING。输出结果:

PB7=0, PB12=1 PB7=1, PB12=1 PB7=0, PB12=1 PB7=1, PB12=1 PB7=0, PB12=1 PB7=1, PB12=1

可见输出端PB12全为1,短时间内根本无法反相。只有把NOP改成delay(1),降低速度后才能成功反相。若使用74HC04的话,即使程序中没有NOP也能完成反相。


【程序1(寄存器版)】

#include <stdio.h> #include <stm32f10x.h> #include <string.h> // 延时n毫秒 void delay(uint16_t nms) { TIM6->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 DS12C887 str=This is a string! A=0x00 B=0x82 C=0x00 D=0x00
D寄存器的最高位为0,看来DS12C887芯片里面的电池早就没电了。。。。。

地址0x33~0x7f这一区域为自由SRAM,可以任意读写,不影响芯片功能。


以下为连线的实物图。我用的是带8MHz晶振的微雪STM32F103VET6核心板做的实验。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/dianzi/2580619.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-08
下一篇 2022-08-08

发表评论

登录后才能评论

评论列表(0条)

保存