上一章學(xué)習(xí)了STM32MP1內(nèi)置RTC外設(shè),了解了Linux系統(tǒng)下RTC驅(qū)動(dòng)框架。一般的應(yīng)用場(chǎng)合使用SOC內(nèi)置的RTC就可以了,而且成本也低,但是在一些對(duì)于時(shí)間精度要求比較高的場(chǎng)合,SOC內(nèi)置的RTC就不適用了。這個(gè)時(shí)候需要根據(jù)自己的應(yīng)用要求選擇合適的外置RTC芯片,正點(diǎn)原子STM32MP1開發(fā)板上板載了一個(gè)RTC芯片:PCF8563,這是一個(gè)IIC接口的外置RTC芯片,本章就來(lái)學(xué)習(xí)一 下如何驅(qū)動(dòng)外置RTC芯片。
PCF8563簡(jiǎn)介
PCF8563簡(jiǎn)介
PCF8563是一個(gè)CMOS RTC芯片,支持時(shí)間和日歷功能,支持可編程的時(shí)鐘輸出、中斷輸出以及低電壓檢測(cè)。PCF8563提供了兩線IIC接口來(lái)傳輸時(shí)間信息,最大傳輸速度為400Kbit/S,在讀寫寄存器的時(shí)候地址自增,PCF8563相關(guān)特性如下:
- 提供年、月、日、星期,時(shí)、分、秒計(jì)時(shí),使用外置32.768Khz晶振。
- 低后備電流:0.25uA,VDD=3.0V,溫度25℃。
- IIC接口,速度最高400KHz。
- 可編程時(shí)鐘輸出,可以供其他設(shè)備使用,可輸出的時(shí)鐘頻率有32.768kHz、1.024kHz、32Hz和1Hz。
- 支持鬧鐘和定時(shí)功能。
- IIC讀地址為0XA3,寫地址為0XA2,也就是IIC器件地址為:0X51。
- 有一個(gè)開漏輸出的中斷引腳。
PCF8563框圖如下圖所示:
簡(jiǎn)單分析一下上圖中的框圖:
- 這是PCF8563的32.768kHz晶振引腳,PCF8563必選要外接32.768kHz晶振。
- 這是PCF8563的IIC引腳,PCF8563通過(guò)IIC接口與主控進(jìn)行通信,因此PCF8563本質(zhì)是個(gè)IIC器件。
- 時(shí)鐘輸出引腳。
- 中斷引腳。
- 前面說(shuō)了,PCF8563是個(gè)IIC器件,因此內(nèi)部就有很多寄存器來(lái)實(shí)現(xiàn)RTC功能,比如配置芯片,讀取時(shí)間信息等。這部分就PCF8563的內(nèi)部寄存器。
PCF8563寄存器詳解
PCF8563有16個(gè)內(nèi)部寄存器,這些寄存器都是8位的。前兩個(gè)寄存器(0x00和0x01)為控
制/狀態(tài)寄存器。0X02-0X08為時(shí)間和日期寄存器,這些寄存器保存著秒、分、時(shí)、日、星期、月和年信息。0X09-0X0C為鬧鐘寄存器,保存鬧鐘信息。0X0D為時(shí)鐘輸出頻率寄存器,0X0E和0X0F這兩個(gè)寄存器時(shí)鐘控制寄存器。注意、時(shí)分秒、年月日、鬧鐘等時(shí)間信息為BCD格式。
接下來(lái)看一下這些寄存器如何使用:
控制狀態(tài)寄存器1(0X00)
寄存器結(jié)構(gòu)如下圖所示:
上圖是控制狀態(tài)寄存器 1,相應(yīng)的位含義如下:
- TEST1(bit7):0,正常模式;1,測(cè)試模式。
- N(bit6,bit4,bit2-0):未使用。
- STOP(bit5):0,RTC時(shí)鐘運(yùn)行;1,RTC時(shí)鐘停止。
- TESTC(bit3):0,正常模式,關(guān)閉POR覆寫;1,使能POR覆寫。
控制狀態(tài)寄存器2(0X01)
寄存器結(jié)構(gòu)如下圖所示:
上圖是控制狀態(tài)寄存器2,相應(yīng)的位含義如下:
- N(bit7-5):未使用。
- TI_TP(bit4):為0的時(shí)候INT引腳取決于TF位,為1的時(shí)候INT引腳輸出指定頻率的脈沖。
- AF(bit3):鬧鐘標(biāo)志位,為1的話表示鬧鐘發(fā)生,寫0清除,寫1無(wú)效。
- TF(bit2):定時(shí)器標(biāo)志位,為1的話表示定時(shí)發(fā)生,寫0清除,寫1無(wú)效。
- AIE(bit1):鬧鐘中斷使能位0,關(guān)閉鬧鐘中斷;1,使能鬧鐘中斷。
- TIE(bit0):定時(shí)器中斷使能位0,關(guān)閉定時(shí)器中斷;1,使能定時(shí)器中斷。
時(shí)間和日期寄存器(0X02-0X08)
接下來(lái)看一下時(shí)間和日期相關(guān)寄存器,一共7個(gè)寄存器,結(jié)構(gòu)如下圖所示:
依次來(lái)看一下上圖中的這些寄存器:
- 0X02:此寄存器為秒鐘寄存器,PCF8563是有低電壓檢測(cè)的,當(dāng)VDD電壓低于最小允許電壓的時(shí)候VL(bit)位就會(huì)置1,表示時(shí)鐘異常,如果電壓正常的話就為0。SECONDS(bit6-0):這7位表示具體的秒數(shù),范圍0~59,為BCD格式。
- 0X03:此寄存器為分鐘寄存器,MINUTES(bit6-0)這7位有效,表示具體的分鐘數(shù),范圍0-59,為BCD格式。
- 0X04:此寄存器為小時(shí)寄存器,HOURS(bit5-0)這6位有效,表示具體的小時(shí)數(shù),范圍0-23,為BCD格式。
- 0X05:此寄存器為日期寄存器,DAYS(bit5-0)這6位有效,表示具體的小時(shí)數(shù),范圍1-31,為BCD格式。
- 0X06:此寄存器為星期寄存器,WEEKDAYS(bit2-0)這3位有效,表示具體的星期,范圍0-6,為BCD格式。0為星期日, ,1為星期一,以此類推,6就是星期六。
- 0X07:此寄存器為月份寄存器,其中C(bit7)為世紀(jì)標(biāo)志位,如果為1的話表示20xx年,為 0的話表示19xx年。MONTHS(bit4-0)這5位有效,表示具體的月份,范圍1-12,分別為1-12月,為BCD格式。
- 0X08:此寄存器為年寄存器,YEARS(bit7-0)這8位有效,表示具體的年份,范圍0-99。
鬧鐘寄存器(0X09-0X0C)
接下來(lái)看一下鬧鐘相關(guān)寄存器,一共4個(gè)寄存器,結(jié)構(gòu)如下圖所示:
依次來(lái)看一下上圖中的這些寄存器:
- 0X09:此寄存器為鬧鐘分鐘寄存器,AE_M(bit7)為分鐘鬧鐘使能位,為0的話使能分鐘鬧鐘,為1的話關(guān)閉。MINUTE_ALARM(bit6-0)這7位表示具體的鬧鐘分鐘,范圍0-59,為BCD格式。
- 0X0A:此寄存器為鬧鐘小時(shí)寄存器,含義和0X09寄存器類似。
- 0X0B:此寄存器為鬧鐘日期寄存器,含義和0X09寄存器類似。
- 0X0C:此寄存器為鬧鐘星期寄存器,含義和0X09寄存器類似。
另外還有時(shí)鐘輸出寄存(0X0D)以及定時(shí)器寄存器(0X0E和0X0F),這里不用PFC8563的時(shí)鐘輸出和定時(shí)器功能,就不講解了。
總體來(lái)說(shuō),PCF8563還是很簡(jiǎn)單的,這是一個(gè)IIC接口的RTC芯片,因此在Linux系統(tǒng)下
就涉及到兩類驅(qū)動(dòng):
- IIC驅(qū)動(dòng),需要IIC驅(qū)動(dòng)框架來(lái)讀寫PCF8563芯片。
- RTC驅(qū)動(dòng),因?yàn)檫@是一個(gè)RTC芯片,因此要用到RTC驅(qū)動(dòng)框架。
如果要用到中斷功能的話,還需要用到Linux系統(tǒng)中的中斷子系統(tǒng),這些前面都有相應(yīng)的實(shí)驗(yàn)講解。所以PCF8563的Linux驅(qū)動(dòng)并不復(fù)雜,而且重點(diǎn)是Linux系統(tǒng)默認(rèn)就已經(jīng)集了PCF8563驅(qū)動(dòng),使用起來(lái)非常簡(jiǎn)單,直接修改設(shè)備樹,添加PCF8563節(jié)點(diǎn)信息,然后使能內(nèi)核的PCF8563驅(qū)動(dòng)即可。
硬件原理圖分析
PCF8563原理圖如下圖所示:
從上圖可以看出,PCF8563連接到了STM32MP157的I2C4接口上,引腳為PZ5、 PZ4。另外, PCF8563的INT引腳連接到了STM32MP157的PI3引腳上。
實(shí)驗(yàn)驅(qū)動(dòng)編寫
修改設(shè)備樹
添加/查找PCF8563使用IO的pinmux配置
PCF8563的IIC接口連接到了STM32MP157的I2C4上,對(duì)應(yīng)的引腳為PZ4和PZ5。另外還有一個(gè)中斷引腳PI3,首先需要在設(shè)備樹中添加這3個(gè)引腳對(duì)應(yīng)的配置信息。
首先添加PZ4和PZ5,打開stm32mp15-pincrtl.dtsi文件,查找一下有沒(méi)有I2C4的引腳配置信息,默認(rèn)是有的,內(nèi)容如下:
示例代碼44.3.1.1 i2c4引腳節(jié)點(diǎn)
1 i2c4_pins_a: i2c4-0 {
2 pins {
3 pinmux = <STM32_PINMUX('Z', 4, AF6)>, /* I2C4_SCL */
4 <STM32_PINMUX('Z', 5, AF6)>; /* I2C4_SDA */
5 bias-disable;
6 drive-open-drain;
7 slew-rate = <0>;
8 };
9 };
10
11 i2c4_pins_sleep_a: i2c4-1 {
12 pins {
13 pinmux = <STM32_PINMUX('Z', 4, ANALOG)>, /* I2C4_SCL */
14 <STM32_PINMUX('Z', 5, ANALOG)>; /* I2C4_SDA */
15 };
16 };
從第3、4行可以看出,I2C4默認(rèn)引腳就是PZ4和PZ5,和本實(shí)驗(yàn)一樣,所以I2C4的引腳不需要修改,直接使用i2c4_pins_a即可。接下來(lái)還需要定義中斷引腳PI3的引腳信息,前面講過(guò)了,如果一個(gè)引腳作為GPIO功能的話可以不用添加此引腳pinctrl信息。
在I2C4節(jié)點(diǎn)下添加pinmux并追加pcf8563子節(jié)點(diǎn)
前面說(shuō)了Linux內(nèi)核內(nèi)部已經(jīng)集成了PCF8563驅(qū)動(dòng),所以肯定有文檔描述如何使用這個(gè)驅(qū)動(dòng)。打開Documentation/devicetree/bindings/rtc/pcf8563.txt,此文檔描述了如何使用Linux內(nèi)核自帶的pcf8563驅(qū)動(dòng),也給出了參考設(shè)備節(jié)點(diǎn),參考此文檔即可。
在stm32mp157d-atk.dts文件,追加I2C4節(jié)點(diǎn),追加如下所示內(nèi)容:
示例代碼 44. 3.1.3 追加 pcf 8563 節(jié)點(diǎn)
1 &i2c4 {
2 pinctrl-names = "default", "sleep";
3 pinctrl-0 = <&i2c4_pins_a>;
4 pinctrl-1 = <&i2c4_pins_sleep_a>;
5 status = "okay";
6
7 pcf8563@51{
8 compatible = "nxp,pcf8563";
9 irq_gpio = <&gpioi 3 IRQ_TYPE_EDGE_FALLING>;
10 reg = <0x51>;
11 };
12 };
第2-4行,設(shè)置IO要使用的pinmux配置。
第7-10行,pcf8563設(shè)備子節(jié)點(diǎn),第8行設(shè)置compatible為“nxp,pcf8563”,這個(gè)是必須的,否則無(wú)法匹配Linux內(nèi)核自帶的pcf8563驅(qū)動(dòng)。從第9行設(shè)置pcf8563中斷引腳為PI3,下降沿觸發(fā)。pcf8563的I2C地址為0X51,因此reg為0X51。
PCF8563驅(qū)動(dòng)使能
上一個(gè)實(shí)驗(yàn)使能了STM32MP157內(nèi)部RTC,為了防止干擾,所以要先關(guān)閉內(nèi)部RTC!配置路徑為:
-> Device Drivers -> Real Time Clock -> STM32 RTC //取消選中 |
如下圖所示:
使能Linux內(nèi)核自帶的PCF8563驅(qū)動(dòng)
接下來(lái)需要使能Linux內(nèi)核自帶的PCF8563驅(qū)動(dòng),配置路徑如下:
-> Device Drivers -> Real Time Clock -> <*> Philips PCF8563/Epson RTC8564 //選中 PCF8563 |
如下圖所示:
配置完成后重新編譯內(nèi)核和設(shè)備樹,得到新的uImage以及stm32mp157d-atk.dtb。
運(yùn)行測(cè)試
使用上面編譯得到的內(nèi)核和設(shè)備樹啟動(dòng)開發(fā)板。當(dāng)系統(tǒng)第一次啟動(dòng),沒(méi)有設(shè)置PCF8563時(shí)間的時(shí)候,啟動(dòng)過(guò)程會(huì)提示如下圖所示信息:
從上圖可以看出,系統(tǒng)已經(jīng)識(shí)別出了PCF8563,說(shuō)明驅(qū)動(dòng)沒(méi)問(wèn)題。但是,這里提示檢測(cè)到低電壓,日期和時(shí)間無(wú)效。這是因?yàn)闆](méi)有設(shè)置時(shí)間,等系統(tǒng)啟動(dòng)成功,然后參考上一篇筆記內(nèi)部RTC的設(shè)置方法設(shè)置RTC時(shí)間,比如這里設(shè)置時(shí)間為2021年5月21號(hào),下午15:52:00,輸入如下命令:
date -s "2021-05-21 15:52:00" //設(shè)置時(shí)間 hwclock -w //保存 |
時(shí)間設(shè)置好以后重啟系統(tǒng),此時(shí)系統(tǒng)log信息如下圖所示:
從上圖可以看出,此時(shí)PCF8563再?zèng)]有提示電壓低的錯(cuò)誤,而且正確的讀出了時(shí)間信
息,整個(gè)開發(fā)板掉電以后PCF8563也會(huì)繼續(xù)計(jì)時(shí),因?yàn)橛幸粋€(gè)紐扣電池供電。
PCF8563驅(qū)動(dòng)分析
上一小節(jié)已經(jīng)測(cè)試了PCF8563,本小節(jié)來(lái)簡(jiǎn)單看一下PCF8563驅(qū)動(dòng)源碼,根據(jù)示例代碼44.3.1.3中的第8行的compatible屬性值可以找到對(duì)應(yīng)到驅(qū)動(dòng)文件,在Linux源碼中搜索字符串“nxp,pcf8563”即可找到對(duì)應(yīng)的驅(qū)動(dòng)文件,驅(qū)動(dòng)文件為drivers/rtc/rtc-pcf8563.c。
PCF8563是個(gè)I2C器件,因此基礎(chǔ)驅(qū)動(dòng)框架是I2C,在rtc-pcf8563.c文件中找到如下所示內(nèi)容:
上述示例代碼就是個(gè)標(biāo)準(zhǔn)的I2C驅(qū)動(dòng)框架,第9-14行的pcf8563_of_match結(jié)構(gòu)體數(shù)組就是設(shè)備樹匹配數(shù)組,第10行的compatible屬性為“nxp,pcf8563”,和設(shè)備樹相匹配。匹配以后第23行的pcf8563_probe函數(shù)就會(huì)執(zhí)行。
接下來(lái)看一下pcf8563_probe函數(shù),函數(shù)源碼如下(有縮略):
示例代碼44.5.2 pcf8563_probe 函數(shù)
1 static int pcf8563_probe(struct i2c_client *client,
2 const struct i2c_device_id *id)
3 {
4 struct pcf8563 *pcf8563;
5 int err;
6 unsigned char buf;
......
13 pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563),
14 GFP_KERNEL);
15 if (!pcf8563)
16 return -ENOMEM;
17
18 i2c_set_clientdata(client, pcf8563);
19 pcf8563->client = client;
20 device_set_wakeup_capable(&client->dev, 1);
21
22 /* Set timer to lowest frequency to save power */
23 buf = PCF8563_TMRC_1_60;
24 err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf);
25 if (err < 0) {
26 dev_err(&client->dev, "%s: write error\n", __func__);
27 return err;
28 }
29
30 /* Clear flags and disable interrupts */
31 buf = 0;
32 err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
33 if (err < 0) {
34 dev_err(&client->dev, "%s: write error\n", __func__);
35 return err;
36 }
37
38 pcf8563->rtc = devm_rtc_allocate_device(&client->dev);
39 if (IS_ERR(pcf8563->rtc))
40 return PTR_ERR(pcf8563->rtc);
41
42 pcf8563->rtc->ops = &pcf8563_rtc_ops;
43 /* the pcf8563 alarm only supports a minute accuracy */
44 pcf8563->rtc->uie_unsupported = 1;
45 pcf8563->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
46 pcf8563->rtc->range_max = RTC_TIMESTAMP_END_2099;
47 pcf8563->rtc->set_start_time = true;
48
49 if (client->irq > 0) {
50 err = devm_request_threaded_irq(&client->dev, client->irq,
51 NULL, pcf8563_irq,
52 IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW,
53 pcf8563_driver.driver.name, client);
54 if (err) {
55 dev_err(&client->dev, "unable to request IRQ %d\n",
56 client->irq);
57 return err;
58 }
59 }
60
61 err = rtc_register_device(pcf8563->rtc);
62 if (err)
63 return err;
......
70 return 0;
71 }
第13行,申請(qǐng)內(nèi)存內(nèi)存,rtc-pcf8563.c定義了一個(gè)pcf8563結(jié)構(gòu)體來(lái)描述PCF8563芯片,所以這里就是申請(qǐng)一個(gè)pcf8563實(shí)例。
第23-36行,初始化PCF8563。
第38行,pcf8563結(jié)構(gòu)體里面有個(gè)rtc成員變量,此成員變量是個(gè)rtc_device結(jié)構(gòu)體指針。
這個(gè)就是上一章講解的RTC驅(qū)動(dòng)框架最核心的rtc_device。這里需要對(duì)這個(gè)rtc指針?lè)峙鋬?nèi)存。
第42行,設(shè)置rtc_device的ops成員變量為pcf8563_rtc_ops,pcf8563_rtc_ops包含了PCF8563的具體操作,包括設(shè)置時(shí)間、讀取時(shí)間、設(shè)置鬧鐘等。
第44-47行,繼續(xù)初始化rtc的其他成員變量。
第49-59行,中斷初始化,PCF8563有個(gè)中斷引腳INT,因此可以使用中斷功能。這里使用devm_request_threaded_irq函數(shù)完成中斷申請(qǐng)已經(jīng)初始化,中斷函數(shù)為pcf8563_irq。
第61行,調(diào)用rtc_register_device函數(shù)向系統(tǒng)注冊(cè)rtc_device,也就是pcf8563。
總結(jié) 一下,pcf8563_probe函數(shù)的核心就是初始化PCF8563,然后使用上一章講的RTC驅(qū)動(dòng)框架來(lái)設(shè)置PCF8563,然后向內(nèi)核注冊(cè)。
接下來(lái)看一下PCF8563的核心:pcf8563_rtc_ops,內(nèi)容如下:
pcf8563_rtc_ops提供了PCF8563的時(shí)間以及鬧鐘讀寫操作函數(shù),應(yīng)用程序?qū)CF8563的所有操作最終都是通過(guò)這些函數(shù)來(lái)完成的。以讀時(shí)間為例,當(dāng)應(yīng)用程序讀取PCF8563當(dāng)前時(shí)間的時(shí)候,.read_time就會(huì)執(zhí)行,在這里就是pcf8563_rtc_read_time,函數(shù)源碼如下(有省略):
第8行,使用pcf8563_read_block_data函數(shù)從PCF8563_REG_ST1寄存器(地址為0X00)開始,連續(xù)讀取9個(gè)寄存器的數(shù)據(jù)。這樣就可以得到PCF8563的控制與狀態(tài)寄存器1和2,以及事件與日期寄存器的值。
第12行,判斷PCF8563的0X02寄存器VL位是否為1,也就是檢查PCF8563是否處于低電壓模式,事件和日期是否有效。
第28-34行,依次獲取PCF8563中的時(shí)間和日期值,這里使用bcd2bin函數(shù)將原始的BCD值轉(zhuǎn)換為時(shí)間值。將獲取到的時(shí)間和日期打包到參數(shù)tm中,tm是個(gè)rtc_time結(jié)構(gòu)體指針變量。
第36行,判斷0X07寄存器的C位(bit7)的值,此位為1的話表示20xx年,為0的話就是19xx年。
可以看出pcf8563_rtc_read_time函數(shù)很簡(jiǎn)單,就是讀取PCF8563內(nèi)部的時(shí)間和日期值,然后將其打包進(jìn)rtc_time里面。其他的函數(shù)大同小異,可以自行分析一下。
至此,PCF8563驅(qū)動(dòng)就簡(jiǎn)單分析完成了,其他IIC接口的RTC芯片驅(qū)動(dòng)基本都是類似的,可以在實(shí)際項(xiàng)目開發(fā)中選擇合適的RTC芯片。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-735237.html
總結(jié)
這里的驅(qū)動(dòng)PCF8563還是比較簡(jiǎn)單的,因?yàn)長(zhǎng)inux內(nèi)核是已經(jīng)寫好了相關(guān)驅(qū)動(dòng)的,只要自己在Linux內(nèi)核配置開啟,然后在設(shè)備樹中添加相應(yīng)的對(duì)應(yīng)的i2c節(jié)點(diǎn)以及GPIO對(duì)應(yīng)的子節(jié)點(diǎn)就可以使用了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-735237.html
到了這里,關(guān)于正點(diǎn)原子嵌入式linux驅(qū)動(dòng)開發(fā)——外置RTC芯片PCF8563的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!