前言
架构做得好,高产似母猪。
接口改一改,效果就出来。
开工第二天,搞一搞SPI驱动DS1302。
关键字:GD32,SPI,DS1302,三线SPI,半双工SPI
SPI
串行外设接口(Serial Peripheral Interface,缩写为SPI)提供了基于SPI协议的数据发送和接收功能,可以工作于主机或从机模式。SPI接口支持具有硬件CRC计算和校验的全双工和单工模式。有些SPI口还支持SPI四线主机模式。
常规的SPI信号描述如下图:
![32d59ad751636c387b040e95049c593f.png](https://i-blog.csdnimg.cn/blog_migrate/8ec8aa1c98a102d3b3815fd8f094bbd9.png)
DS1302
DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,并且具有闰年补偿等多种功能。(百度百科)
DS1302的管脚配置:
![86b95ff5b38c62b005ee7172d645a677.png](https://i-blog.csdnimg.cn/blog_migrate/1d7b1cca8965c27d175b4209be52d449.png)
DS1302的时序:
![ebc2fdc6ecb2e6e7180a2d6015778597.png](https://i-blog.csdnimg.cn/blog_migrate/efb1e0025e49e36857463d012e9f4136.png)
可以看到:除了CE高低电平不一样,这个时序跟半双工的SPI是一样的。
![1ab4770f93d55c50b87ddbe187750af5.png](https://i-blog.csdnimg.cn/blog_migrate/7fa9349874d5ae151123e2286799d5f3.png)
DS1302的寄存器配置:
![095ba1acaf8097af3edff0fec88d403b.png](https://i-blog.csdnimg.cn/blog_migrate/a0b20ab5a5f60210cf9fb16afbf69f2c.png)
DS1302的时钟频率:
![4df02ec988130997c90f0056c9bebfdf.png](https://i-blog.csdnimg.cn/blog_migrate/699b934d3b647d5accb1522afaf658f6.png)
三线SPI驱动DS1302
半双工SPI的基本初始化:
单线发送模式
数据8位
主机模式
时钟空闲低电平,第一个边沿读数据
NSS Soft
分频:根据DS1302的配置来,一般1M多比较合适。
LSB
发送流程:
设置为发送模式
CE使能
发送地址
发送数据
CE失能
接收流程:
设置为发送模式
CE使能
发送地址
设置为接收模式
等待数据接收完成
CE失能
切换为发送模式,掐断SPI时钟信号
读数据,等待SPI收工
模拟IO驱动DS1302
略(曾经写过,就不贴了)
SPI驱动DS1302
坑爹的小插曲:
SPI一般是AF5,结果用的SPI1管脚是PB13,PB14,PB15,他们是AF6,被坑了2个小时,真扯。
巨讨厌调试硬件,真希望芯片厂商设计硬件时能更多一些软件工程师的设计idea。
SPI初始化:
#define DS1302_USING_SPI
/* SPI0:PA5-SCK-SPI_SCK PA6-CE-SPI_MISO PA7-DATA-SPI_MOSI */
/* SPI1:PB13-SCK-SPI_SCK PB14-CE-SPI_MISO PB15-DATA-SPI_MOSI */
#define CURRENT_SPI SPI1
#define CURRENT_SPI_RCU RCU_SPI1
#define RTC_RCU RCU_GPIOB
#define RTC_GPIO GPIOB
#define RTC_SCK_PIN GPIO_PIN_13
#define RTC_CE_PIN GPIO_PIN_14
#define RTC_DATA_PIN GPIO_PIN_15
#ifdef DS1302_USING_SPI
#define READ_MODE spi_disable(CURRENT_SPI); \
spi_bidirectional_transfer_config(CURRENT_SPI, SPI_BIDIRECTIONAL_RECEIVE); \
spi_enable(CURRENT_SPI);
#define WRITE_MODE spi_disable(CURRENT_SPI); \
spi_bidirectional_transfer_config(CURRENT_SPI, SPI_BIDIRECTIONAL_TRANSMIT); \
spi_enable(CURRENT_SPI);
#define CE_HIGH gpio_bit_set(RTC_GPIO, RTC_CE_PIN);
#define CE_LOW gpio_bit_reset(RTC_GPIO, RTC_CE_PIN);
#else
#endif
void spi1_init(void)
{
spi_parameter_struct spi_init_struct;
spi_i2s_deinit(CURRENT_SPI);
rcu_periph_clock_enable(CURRENT_SPI_RCU);
/* SPI_MOSI */
gpio_init_af_mode(RTC_RCU, RTC_GPIO, RTC_DATA_PIN, GPIO_OSPEED_50MHZ, GPIO_AF_6);
/* SPI_SCK */
gpio_init_af_mode(RTC_RCU, RTC_GPIO, RTC_SCK_PIN, GPIO_OSPEED_50MHZ, GPIO_AF_6);
/* RTC CE */
gpio_init_output_mode(RTC_RCU, RTC_GPIO, RTC_CE_PIN, GPIO_OSPEED_50MHZ, 0);
CE_LOW;
/* SPI parameter config */
spi_init_struct.trans_mode = SPI_TRANSMODE_BDTRANSMIT;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = SPI_PSC_16;
spi_init_struct.endian = SPI_ENDIAN_LSB;
spi_init(CURRENT_SPI, &spi_init_struct);
/* enable */
spi_enable(CURRENT_SPI);
}
DS1302初始化:
#define DS1302_UNLOCK write_ds1302(0x8e, 0x00);
#define DS1302_LOCK write_ds1302(0x8e, 0x80);
void rtc_time_init(void)
{
uint8_t hour;
uint8_t hour_24 = 0;
if (read_ds1302(READ_FLAG_ADDR) != RTC_INIT_FLAG)
{
write_ds1302(0x84, hour_24);
set_rtc(init_time);
return;
}
hour = read_ds1302(0x85);
if (hour > 0x80)
{
// 12小时制转为24小时制
hour_24 = (hour > 0xA0)?(hour&0x1F)|DEC2BCD(12):(hour&0x1F);
write_ds1302(0x84, hour_24);
}
}
读写接口:
static void spi_rtc_send_byte(uint8_t byte)
{
/* loop while data register in not empty */
while(RESET == spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_TBE));
/* send byte through the SPI peripheral */
spi_i2s_data_transmit(CURRENT_SPI, byte);
while(spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_TRANS));
}
static void write_ds1302(uint8_t address, uint8_t data)
{
WRITE_MODE;
CE_HIGH;
spi_rtc_send_byte(address);
spi_rtc_send_byte(data);
CE_LOW;
}
static uint8_t read_ds1302(uint8_t address)
{
uint8_t data;
/* send addr */
WRITE_MODE;
CE_HIGH;
spi_rtc_send_byte(address);
/* recive data */
READ_MODE;
while(RESET == spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_RBNE));
CE_LOW;
WRITE_MODE;
data = spi_i2s_data_receive(CURRENT_SPI);
while(spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_TRANS));
return data;
}
void set_rtc(time_t t)
{
uint8_t year = t.year - YEAR_BASE;
if(year >= 100) year = 0;
DS1302_UNLOCK;
/* 写入已经设置时间标记 */
write_ds1302(WRITE_FLAG_ADDR, RTC_INIT_FLAG);
write_ds1302(0x80, DEC2BCD(t.second));
write_ds1302(0x82, DEC2BCD(t.minute));
write_ds1302(0x84, DEC2BCD(t.hour));
write_ds1302(0x86, DEC2BCD(t.day));
write_ds1302(0x88, DEC2BCD(t.month));
write_ds1302(0x8c, DEC2BCD(year));
write_ds1302(0x8a, DEC2BCD(t.week));
DS1302_LOCK;
}
time_t get_rtc(void)
{
time_t t;
uint16_t year = read_ds1302(0x8d);
uint8_t month = read_ds1302(0x89);
uint8_t day = read_ds1302(0x87);
uint8_t hour = read_ds1302(0x85);
uint8_t minute = read_ds1302(0x83);
uint8_t second = read_ds1302(0x81);
uint8_t week = read_ds1302(0x8b);
t.year = BCD2DEC(year) + YEAR_BASE;
t.month = BCD2DEC(month);
t.day = BCD2DEC(day);
t.hour = BCD2DEC(hour);
t.minute = BCD2DEC(minute);
t.second = BCD2DEC(second);
t.week = BCD2DEC(week);
return t;
}
测试结果
DS1302星期(寄存器0x8B)的SPI波形:
![820e61a85e03dced612f95b34413b0f0.png](https://i-blog.csdnimg.cn/blog_migrate/8da51b4206103ac9db8855126e5e542f.png)
串口数据:
![2a8f72edd482039296eae002ea0fbc70.gif](https://i-blog.csdnimg.cn/blog_migrate/06fd29846ab1dc988900a6b1ebc6a58b.gif)
![ea0139739689ea5809d04b811edb41a3.gif](https://i-blog.csdnimg.cn/blog_migrate/4c598bdb39229d4a25a7a6b115e44d43.gif)
![3f0b3ebc597f77c2d430df65b683fbb2.png](https://i-blog.csdnimg.cn/blog_migrate/4fd0df47aa96d53de89b59e354da34d3.png)
![08849e5919fe5d58dd436473831099cb.gif](https://i-blog.csdnimg.cn/blog_migrate/e4964141de5086b3bae35e9d119e31e0.gif)