因项目需要RTC由原来的DS1302(这个芯片的代码还蛮多的,就不挂了)。更改为了SD2405ALPI-G,看网上类似的资源较少,与诸君分享一下,有不足之处也希望诸君指正。
对于SD2405模块的资料可参见https://wenku.baidu.com/view/cbebaea1b0717fd5360cdc13.html。
首先是IIC协议(资料也蛮多,但是挂一下代码,这个是自己验证过可以使用的,我也会在代码后面大致写一下为啥还有一些要注意的小小细节),
1 void IIC_For_Init(void) 2 { 3 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); 4 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); 5 6 GPIO_InitStructure.GPIO_Pin=IIC_SCL_PIN; 7 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 8 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 9 GPIO_Init(RCC_APB2Periph_GPIOB,&GPIO_InitStructure); 10 11 GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN; 12 GPIO_Init(RCC_APB2Periph_GPIOB,&GPIO_InitStructure); 13 14 IIC_SCL=1; 15 IIC_SDA=1; 16 } 17 18 19 void SDA_OUT_For(void) 20 { 21 GPIO_InitTypeDef GPIO_InitStructure; 22 23 GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN; 24 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 25 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 26 GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure); 27 } 28 29 30 void SDA_IN_For(void) 31 { 32 GPIO_InitTypeDef GPIO_InitStructure; 33 34 GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN; 35 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; 36 GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure); 37 } 38 39 40 void IIC_For_Start(void) 41 { 42 43 SDA_OUT_For(); 44 IIC_SDA=1; 45 IIC_SCL=1; 46 delay_us(5); 47 IIC_SDA=0;//START:when CLK is high,DATA change form high to low 48 delay_us(6); 49 IIC_SCL=0; 50 } 51 52 void IIC_For_Stop(void) 53 { 54 SDA_OUT_For(); 55 IIC_SCL=0; 56 IIC_SDA=0;//STOP:when CLK is high DATA change form low to high 57 IIC_SCL=1; 58 delay_us(6); 59 IIC_SDA=1; 60 delay_us(6); 61 } 62 63 u8 IIC_For_Wait_Ack(void) 64 { 65 u8 tempTime=0; 66 67 IIC_SDA=1; 68 delay_us(1); 69 SDA_IN_For(); 70 IIC_SCL=1; 71 delay_us(1); 72 while(READ_SDA) 73 { 74 tempTime++; 75 if(tempTime>250) 76 { 77 IIC_For_Stop(); 78 return 1; 79 } 80 } 81 IIC_SCL=0; 82 return 0; 83 } 84 85 void IIC_For_Ack(void) 86 { 87 IIC_SCL=0; 88 SDA_OUT_For(); 89 IIC_SDA=0; 90 delay_us(2); 91 IIC_SCL=1; 92 delay_us(5); 93 IIC_SCL=0; 94 } 95 96 97 void IIC_For_NAck(void) 98 { 99 IIC_SCL=0; 100 SDA_OUT_For(); 101 IIC_SDA=1; 102 delay_us(2); 103 IIC_SCL=1; 104 delay_us(5); 105 IIC_SCL=0; 106 } 107 108 109 void IIC_For_Send_Byte(u8 txd) 110 { 111 u8 t; 112 SDA_OUT_For(); 113 IIC_SCL=0; 114 for(t=0;t<8;t++) 115 { 116 if((txd&0x80)>0) //0x80 1000 0000 117 IIC_SDA=1; 118 else 119 IIC_SDA=0; 120 txd<<=1; 121 delay_us(2); 122 IIC_SCL=1; 123 delay_us(2); 124 IIC_SCL=0; 125 delay_us(2); 126 } 127 } 128 129 u8 IIC_For_Read_Byte(u8 ack) 130 { 131 u8 i,receive=0; 132 SDA_IN_For(); 133 for(i=0;i<8;i++ ) 134 { 135 IIC_SCL=0; 136 delay_us(2); 137 IIC_SCL=1; 138 receive<<=1; 139 if(READ_SDA)receive++; 140 delay_us(1); 141 } 142 if (!ack) 143 IIC_For_NAck(); 144 else 145 IIC_For_Ack(); 146 return receive; 147 }
需要注意的有几点:
1、IIC_SDA及IIC_SCL,在这里我使用的是位带操作,https://www.cnblogs.com/leo0621/p/9494290.html,这位博友讲的超详细;
2、关于第一行与第二行的代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
当时使用了JTAG的管脚(PB3/JTDO、PB4/JNTRST),而JTAG与SWD在默认情况下是开启的,需要关掉JTAG管脚并使其作为普通的IO口,这也是调试了半天才想到的一个问题,若不是使用JTAG,则只需要对RCC_APB2Periph_GPIOB进行使能即可,以免误伤;
3、对于SDA_IN_For()和SDA_OUT_For(),首先此处的命名是为了避免重复(因为还有另一个IIC文件来进行AT24C02),初始化采用了分别设置的方法,OUT下为推挽输出,在IN下为上拉输入。在这里值得一提的是,如果在SDA对应的管脚有上拉电阻在,则可以在最开始的IO配置中,将SDA脚配置为浮空输入,不需要再分别设置SDA_IN_For()和SDA_OUT_For()两个函数。
对于SD2405ALPI-G的函数如下:
1 #include "sd2405_driver.h" 2 3 u8 Time_Register[]={ 4 SD2405_ADDR_YEAR, 5 SD2405_ADDR_MONTH, 6 SD2405_ADDR_DAY, 7 SD2405_ADDR_HOUR, 8 SD2405_ADDR_MIN, 9 SD2405_ADDR_SEC, 10 SD2405_ADDR_WEEK 11 }; 12 13 u8 Time_Init[]={ 14 0x19, //year 2019 15 0x05, //month 5 16 0x27, //day 27 17 0x12, //hour 12 18 0x30, //minute 30 19 0x59, //second 59 20 0x06 //week 6 21 }; 22 23 void SD2405_Init(void) 24 { 25 IIC_For_Init(); 26 } 27 28 void SD2405_WriteOneByte(u8 WriteAddr , u8 Data) 29 { 30 IIC_For_Start(); 31 32 IIC_For_Send_Byte(0x64); 33 IIC_For_Wait_Ack(); 34 35 IIC_For_Send_Byte(WriteAddr); 36 IIC_For_Wait_Ack(); 37 38 IIC_For_Send_Byte(Data); 39 IIC_For_Wait_Ack(); 40 41 IIC_For_Stop(); 42 43 delay_ms(10); 44 } 45 46 u8 SD2405_ReadOneByte(u8 ReadAddr) 47 { 48 u8 temp; 49 50 IIC_For_Start(); 51 52 IIC_For_Send_Byte(0x64); 53 IIC_For_Wait_Ack(); 54 55 IIC_For_Send_Byte(ReadAddr); 56 IIC_For_Wait_Ack(); 57 58 IIC_For_Start(); 59 60 IIC_For_Send_Byte(0x65); 61 IIC_For_Wait_Ack(); 62 63 temp = IIC_For_Read_Byte(0); 64 65 IIC_For_Stop(); 66 67 return temp; 68 } 69 70 71 void Write_On(void) 72 { 73 SD2405_WriteOneByte(SD2405_ADDR_CTR2,0x80); 74 SD2405_WriteOneByte(SD2405_ADDR_CTR1,0x84); 75 } 76 77 void Write_Off(void) 78 { 79 SD2405_WriteOneByte(SD2405_ADDR_CTR1,0x00); 80 SD2405_WriteOneByte(SD2405_ADDR_CTR2,0x52); 81 } 82 83 void SD2405_Time_Init(void) 84 { 85 u8 i = 0; 86 Write_On(); 87 for(i=0;i<7;i++){ 88 SD2405_WriteOneByte(Time_Register[i],Time_Init[i]); 89 } 90 Write_Off(); 91 } 92 93 void SD2405_Read_Time(void) 94 { 95 u8 i = 0; 96 for(i=0;i<7;i++){ 97 Time_Init[i]=SD2405_ReadOneByte(Time_Register[i]); 98 } 99 }
首先对于读写来说,0110001?,前7位为寻址字节,其中?表示可为1或0,当为0时为写,1时为读。当?为0时即为0x64,当?为1时为0x65;对于读写操作只需要符合IIC读写方式即可。需要注意的是,要进行Write_On和Write_Off的切换,即可参见说明书第8页中的说明,对于写允许的操作,先WRTC1为1再使WRTC2和WRTC3为1,故在Write_On中为0x80,0x84(应注意CTR1寄存器中有的是WRTC3位和WRTC2位,CTR2中有的是WRTC1位,两个寄存器是没按常理出牌的,不要写颠倒);而写禁止操作则需要先让WRTC2和WRTC3为0再让WRTC1为0,故分别为0x00和0x52。一定要先Write_On再写再Write_Off。
在这里只是进行一个简单的写入并且不断读出并在OLED上进行显示的过程(当然OLED显示的代码没挂,主题还是SD2405嘛),对于报警、时间调整等更复杂的功能,还没做但是也期待与诸君有更深入的讨论。