SylixOS中怎么實(shí)現(xiàn)EEPROM設(shè)備驅(qū)動(dòng),很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。
城中ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!
創(chuàng)新互聯(lián)建站致力于互聯(lián)網(wǎng)品牌建設(shè)與網(wǎng)絡(luò)營(yíng)銷,包括成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、SEO優(yōu)化、網(wǎng)絡(luò)推廣、整站優(yōu)化營(yíng)銷策劃推廣、電子商務(wù)、移動(dòng)互聯(lián)網(wǎng)營(yíng)銷等。創(chuàng)新互聯(lián)建站為不同類型的客戶提供良好的互聯(lián)網(wǎng)應(yīng)用定制及解決方案,創(chuàng)新互聯(lián)建站核心團(tuán)隊(duì)10余年專注互聯(lián)網(wǎng)開(kāi)發(fā),積累了豐富的網(wǎng)站經(jīng)驗(yàn),為廣大企業(yè)客戶提供一站式企業(yè)網(wǎng)站建設(shè)服務(wù),在網(wǎng)站建設(shè)行業(yè)內(nèi)樹(shù)立了良好口碑。
1.開(kāi)發(fā)環(huán)境
操作系統(tǒng):SylixOS
編程環(huán)境:RealEvo-IDE3.1.5
硬件平臺(tái):SAMA5D2 Xplained開(kāi)發(fā)板
2.EEPROM簡(jiǎn)介
EEPROM,或?qū)懽鱁2PROM,全稱電子抹除式可復(fù)寫只讀存儲(chǔ)器 (英語(yǔ):Electrically-Erasable Programmable Read-Only Memory),是一種可以通過(guò)電子方式多次復(fù)寫的半導(dǎo)體存儲(chǔ)設(shè)備。相比EPROM,EEPROM不需要用紫外線照射,也不需取下,就可以用特定的電壓,來(lái)抹除芯片上的信息,以便寫入新的數(shù)據(jù)。
2.1 存儲(chǔ)結(jié)構(gòu)及設(shè)備地址
本篇使用的EEPROM芯片型號(hào)是AT24MAC402,該芯片提供2Kbit串行電可擦除可編程的存儲(chǔ)單元,即256 bytes,并可通過(guò)I2C兼容的串行接口(TWI)進(jìn)行讀寫操作。此外,AT24MAC402可用來(lái)存放全球唯一的MAC或EUI地址(EUI-48)。其內(nèi)部存儲(chǔ)組織結(jié)構(gòu)如圖 2-1所示。
圖 2-1 AT24MAC402內(nèi)部存儲(chǔ)結(jié)構(gòu)
由圖 2-1可知,AT24MAC402提供了128-bit Serial Number和48-bit(9Ah-9Fh)的擴(kuò)展存儲(chǔ)部分用來(lái)存儲(chǔ)序列號(hào)和全球唯一的MAC或EUI地址。作為I2C從設(shè)備,可通過(guò)兩個(gè)不同的設(shè)備地址訪問(wèn)EEPROM的這兩部分(標(biāo)準(zhǔn)和擴(kuò)展)的內(nèi)部存儲(chǔ)地址。AT24MAC402的芯片手冊(cè)對(duì)這兩部分的編址如圖 2-2所示。
圖 2-2 設(shè)備地址
其中Bit[3:1]由硬件引腳電平?jīng)Q定,在沒(méi)有設(shè)置寫保護(hù)的情況下,對(duì)于標(biāo)準(zhǔn)EEPROM可進(jìn)行讀寫操作,而擴(kuò)展部分僅支持讀操作。SAMA5D2開(kāi)發(fā)板EEPROM的電路圖如圖 2-3所示。
圖 2-3 EEPROM電路圖連線
結(jié)合圖 2-2可知EEPROM標(biāo)準(zhǔn)部分的設(shè)備地址是‘1010100’,即0x54;擴(kuò)展部分的設(shè)備地址是‘1011100’,即0x5C。
2.2 操作模式
2.2.1 讀操作
標(biāo)準(zhǔn)EEPROM部分和擴(kuò)展部分均支持讀操作,EEPROM支持以下三種類型的讀操作:
當(dāng)前地址讀:在當(dāng)前地址讀操作方式時(shí)無(wú)需發(fā)送讀字節(jié)地址,每次只將當(dāng)前地址所存數(shù)據(jù)讀出,片內(nèi)地址始終保持自加,直到讀完整個(gè)EEPROM后又回到0地址。
隨機(jī)地址讀:主設(shè)備發(fā)送有效從設(shè)備內(nèi)部地址,并且從設(shè)備發(fā)送響應(yīng)信號(hào)后將會(huì)將該內(nèi)部地址處的數(shù)據(jù)通過(guò)I2C發(fā)送給主設(shè)備。
順序讀:多字節(jié)連續(xù)讀操作既可以是當(dāng)前地址讀,也可以是隨機(jī)地址讀,每次處理器接收到一字節(jié)數(shù)據(jù)都返回一個(gè)ACK,EEPROM接收到此ACK后會(huì)自動(dòng)地址加1,接著輸出下一個(gè)字節(jié)數(shù)據(jù),直到處理器返回NO ACK時(shí),讀過(guò)程結(jié)束。
2.2.2 寫操作
標(biāo)準(zhǔn)EEPROM部分,在寫保護(hù)被禁止的情況下提供寫操作,并且支持以下兩種寫操作:
字節(jié)寫:按字節(jié)寫時(shí)通常在向EEPROM發(fā)送設(shè)備地址并收到應(yīng)答信號(hào)后,發(fā)送寫字節(jié)地址再次收到ACK后開(kāi)始寫數(shù)據(jù),最后發(fā)送停止位結(jié)束寫操作。
頁(yè)寫:寫頁(yè)時(shí)EEPROM可一次連續(xù)寫入整頁(yè)數(shù)據(jù)(一頁(yè)為16字節(jié))。其發(fā)地址過(guò)程與寫字節(jié)時(shí)完全相同。不同的是,當(dāng)寫完一個(gè)數(shù)據(jù)字節(jié)后,處理器發(fā)不發(fā)停止?fàn)顟B(tài),而是在應(yīng)答信號(hào)后繼續(xù)寫入數(shù)據(jù),每一個(gè)字節(jié)接收完畢后,EEPROM都返回一個(gè)ACK,一直到寫完整頁(yè)。如果頁(yè)寫時(shí)寫入數(shù)超出該物理頁(yè)邊界,則超出數(shù)據(jù)將重新寫入頁(yè)首地址覆蓋之前所寫數(shù)據(jù)。
3.技術(shù)實(shí)現(xiàn)
本篇通過(guò)內(nèi)核模塊的方式實(shí)現(xiàn)EEPROM的設(shè)備驅(qū)動(dòng)。
EEPROM驅(qū)動(dòng)的編寫同樣是實(shí)現(xiàn)設(shè)備文件操作控制塊結(jié)構(gòu)體file_operations的成員函數(shù),在EEPROM設(shè)備驅(qū)動(dòng)中主要實(shí)現(xiàn)了__e2promOpen、__e2promClose、__e2promRead、__e2promWrite、__e2promIoctl函數(shù)功能,__e2promIoctl函數(shù)用來(lái)設(shè)置待訪問(wèn)的EEPROM的內(nèi)部地址。
應(yīng)用程序可以通過(guò)訪問(wèn)標(biāo)準(zhǔn)文件I/O函數(shù)來(lái)讀寫EEPROM設(shè)備,在讀寫EEPROM設(shè)備前,可調(diào)用lseek函數(shù)設(shè)置要讀/寫的eeprom內(nèi)部寄存器地址,然后調(diào)用標(biāo)準(zhǔn)文件I/O對(duì)該內(nèi)部地址進(jìn)行讀/寫操作。
EEPROM的讀寫功能,實(shí)質(zhì)上是調(diào)用I2C設(shè)備發(fā)送接口的方式實(shí)現(xiàn)的。這里使用字符驅(qū)動(dòng)的框架來(lái)實(shí)現(xiàn)EEPROM的讀寫操作。由于標(biāo)準(zhǔn)EEPROM和擴(kuò)展部分的設(shè)備地址不同,但是對(duì)這兩部分的操作是一樣的,因此本篇僅給出標(biāo)準(zhǔn)EEPROM設(shè)備的驅(qū)動(dòng)實(shí)現(xiàn)。
標(biāo)準(zhǔn)EEPROM設(shè)備文件操作結(jié)構(gòu)體如程序清單 3-1所示。
程序清單 3-1 e2prom設(shè)備文件操作集
/********************************************************************************************************* ** e2prom設(shè)備文件操作集 *********************************************************************************************************/ struct file_operations GfileOperate = { .fo_open = __e2promOpen, .fo_close = __e2promClose, .fo_read = __e2promRead, .fo_write = __e2promWrite, .fo_ioctl = __e2promIoctl };
通過(guò)調(diào)用標(biāo)準(zhǔn)I/O函數(shù),可最終調(diào)用到file_operations結(jié)構(gòu)體中的對(duì)應(yīng)的成員函數(shù)。
3.1 讀操作
__e2promRead讀取EEPROM內(nèi)部數(shù)據(jù),其實(shí)現(xiàn)如程序清單 3-2所示。
程序清單 3-2 __e2promRead實(shí)現(xiàn)
/********************************************************************************************************* ** 函數(shù)名稱: __e2promRead ** 功能描述: 讀取eeprom設(shè)備 ** 輸 入 : pvArg 版本類型選擇參數(shù) ** pcBuffer 緩沖區(qū) ** stMaxByte 緩沖區(qū)大小 ** 輸 出 : ERROR *********************************************************************************************************/ static ssize_t __e2promRead(PVOID pvArg, PCHAR pcBuffer, size_t stMaxByte) { UINT32 uiRet; if(!pcBuffer) { return PX_ERROR; } uiRet = __at24xxRead(Gi2cDev, Goffset, (UINT8 *)pcBuffer, stMaxByte); Goffset = (Goffset + stMaxByte) % EEPROM_MEM_SIZE; /* 內(nèi)部地址計(jì)數(shù)器保存值 */ return (uiRet == ERROR_NONE) ? stMaxByte:PX_ERROR; }
__e2promRead將會(huì)調(diào)用at24xxRead函數(shù)實(shí)現(xiàn)讀操作,at24xxRead實(shí)現(xiàn)如程序清單 3-3所示。
程序清單 3-3 at24xxRead實(shí)現(xiàn)
/********************************************************************************************************* ** 函數(shù)名稱: __at24xxRead ** 功能描述: AT24xx 寄存器讀函數(shù) ** 輸 入 : pI2cDev i2c設(shè)備 ** RegAddress 寄存器地址 ** buf 數(shù)據(jù)接收緩沖區(qū) ** len 需要讀取的數(shù)據(jù)長(zhǎng)度 ** 輸 出 : 返回 0 表示函數(shù)執(zhí)行成功 *********************************************************************************************************/ static int __at24xxRead (PLW_I2C_DEVICE pI2cDev, UINT8 ucRegAddress, UINT8 *ucBuf, UINT uiLen) { INT iError; LW_I2C_MESSAGE i2cMsgs[2] = { { .I2CMSG_usAddr = pI2cDev->I2CDEV_usAddr, .I2CMSG_usFlag = 0, /* 0表示寫操作 */ .I2CMSG_usLen = sizeof(ucRegAddress), .I2CMSG_pucBuffer = &ucRegAddress, /* 先寫要讀的寄存器地址 */ }, { .I2CMSG_usAddr = pI2cDev->I2CDEV_usAddr, .I2CMSG_usFlag = LW_I2C_M_RD, /* 表示讀操作 */ .I2CMSG_usLen = uiLen, .I2CMSG_pucBuffer = ucBuf, /* 接著讀操作 */ } }; iError = API_I2cDeviceTransfer(pI2cDev, i2cMsgs, 2); if (iError < 0) { return (PX_ERROR); } return (ERROR_NONE); }
實(shí)質(zhì)上,應(yīng)用層調(diào)用read函數(shù),最終是調(diào)用的API_I2cDeviceTransfer函數(shù)實(shí)現(xiàn)接收與發(fā)送操作。
3.2 寫操作
__e2promWrite向EEPROM寫入數(shù)據(jù),其實(shí)現(xiàn)如程序清單 3-4所示。
程序清單 3-4 e2promWrite實(shí)現(xiàn)
/********************************************************************************************************* ** 函數(shù)名稱: __e2promWrite ** 功能描述: 寫eeprom設(shè)備 ** 輸 入 : pvArg 版本類型選擇參數(shù) ** pcBuffer 緩沖區(qū) ** stMaxByte 緩沖區(qū)大小 ** 輸 出 : ERROR *********************************************************************************************************/ static ssize_t __e2promWrite(PVOID pvArg, PCHAR pcBuffer, size_t stMaxByte) { UINT32 uiRet; if(!pcBuffer) { return PX_ERROR; } uiRet = __at24xxWrite (Gi2cDev, Goffset, (UINT8 *)pcBuffer, stMaxByte); Goffset = (Goffset + stMaxByte) % EEPROM_MEM_SIZE; /* 內(nèi)部地址計(jì)數(shù)器保存值 */ return (uiRet == ERROR_NONE) ? stMaxByte:PX_ERROR; }
__e2promWrite將會(huì)調(diào)用at24xxWrite函數(shù)實(shí)現(xiàn)EEPROM的寫操作,at24xxWrite實(shí)現(xiàn)如程序清單 3-5所示。
程序清單 3-5 at24xxWrite實(shí)現(xiàn)
/********************************************************************************************************* ** 函數(shù)名稱: __at24xxWrite ** 功能描述: AT24xx 寄存器寫函數(shù) ** 輸 入 : pI2cDev i2c設(shè)備 ** RegAddress 寄存器地址 ** buf 需要寫入寄存器的數(shù)據(jù) ** len 寫入數(shù)據(jù)長(zhǎng)度 ** 輸 出 : 返回 0 表示函數(shù)執(zhí)行成功 *********************************************************************************************************/ static int __at24xxWrite (PLW_I2C_DEVICE pI2cDev, UINT8 ucRegAddress, UINT8 *ucBuf, UINT uiLen) { INT iError; if(!pI2cDev) { return PX_ERROR; } /* * 發(fā)送緩存大小:至少為(數(shù)據(jù)+地址)字節(jié)數(shù) */ UINT8 *pui2cBuf = (UINT8 *)malloc(uiLen+1); LW_I2C_MESSAGE i2cMsgs[1] = { { .I2CMSG_usAddr = pI2cDev->I2CDEV_usAddr, .I2CMSG_usFlag = 0, /* 0表示寫操作 */ .I2CMSG_usLen = uiLen + sizeof(ucRegAddress), /* (數(shù)據(jù)+地址)字節(jié)數(shù) */ .I2CMSG_pucBuffer = pui2cBuf, }, }; /* * 發(fā)送緩存開(kāi)頭存放的是地址信息,然后才是數(shù)據(jù) */ pui2cBuf[0] = ucRegAddress; memcpy(&pui2cBuf[1], &ucBuf[0], uiLen); iError = API_I2cDeviceTransfer(pI2cDev, i2cMsgs, 1); if (iError < 0) { free(pui2cBuf); printk(KERN_ERR "__at24xxWrite(): failed to i2c transfer!\n"); return (PX_ERROR); } free(pui2cBuf); return (ERROR_NONE); }
實(shí)質(zhì)上,應(yīng)用層調(diào)用write函數(shù),最終是調(diào)用的API_I2cDeviceTransfer函數(shù)實(shí)現(xiàn)接收與發(fā)送操作。
3.3 設(shè)置讀寫地址
通過(guò)實(shí)現(xiàn)__e2promIoctl函數(shù),完成設(shè)置待讀/寫的EEPROM的內(nèi)部寄存器地址,其實(shí)現(xiàn)如程序清單 3-6所示。
程序清單 3-6 __e2promIoctl實(shí)現(xiàn)
/********************************************************************************************************* ** 函數(shù)名稱: __e2promIoctl ** 功能描述: 控制eeprom設(shè)備 ** 輸 入 : pdevhdrHdr 設(shè)備頭 ** iCmd 命令 ** lArg 命令參數(shù) ** 輸 出 : ERROR *********************************************************************************************************/ static INT __e2promIoctl(PLW_DEV_HDR pdevhdrHdr, INT iCmd, LONG lArg) { INT iError; struct stat *pstat; switch(iCmd) { case FIOSEEK: /* 獲取e2prom內(nèi)部地址偏移 */ Goffset = *(off_t *)lArg; break; case FIOFSTATGET: /* 獲得文件屬性 */ pstat = (struct stat *)lArg; pstat->st_dev = (dev_t)pdevhdrHdr; pstat->st_ino = (ino_t)0; /* 相當(dāng)于唯一節(jié)點(diǎn) */ pstat->st_mode = 0644 | S_IFCHR; /* 默認(rèn)屬性 */ pstat->st_nlink = 1; pstat->st_uid = 0; pstat->st_gid = 0; pstat->st_rdev = 1; pstat->st_size = 0; pstat->st_blksize = 0; pstat->st_blocks = 0; pstat->st_atime = API_RootFsTime(LW_NULL); /* 默認(rèn)使用 root fs 基準(zhǔn)時(shí)間 */ pstat->st_mtime = API_RootFsTime(LW_NULL); pstat->st_ctime = API_RootFsTime(LW_NULL); break; default: errno = ENOSYS; iError = PX_ERROR; break; } return ERROR_NONE; }
通過(guò)在應(yīng)用層調(diào)用lseek,可以調(diào)用到底層的__e2promIoctl函數(shù),在__e2promIoctl函數(shù)中通過(guò)給全局變量Goffset賦值,在調(diào)用read/write函數(shù)時(shí),底層相應(yīng)的__e2promRead/ __e2promWrite便可獲得Goffset的偏移值,進(jìn)而讀取/寫入到EEPROM內(nèi)部寄存器中。
3.4 驅(qū)動(dòng)模塊初始化及卸載
驅(qū)動(dòng)模塊初始化實(shí)現(xiàn)如程序清單 3-7所示。
程序清單 3-7 模塊初始化
/********************************************************************************************************* ** 函數(shù)名稱: module_init ** 功能描述: 驅(qū)動(dòng)加載模塊 ** 輸 入 : NONE ** 輸 出 : ERROR_CODE *********************************************************************************************************/ int module_init (void) { printk("hello_module init!\n"); INT iDrvNum = API_IosDrvInstallEx(&GfileOperate); /* 安裝驅(qū)動(dòng)程序 */ API_IosDevAdd (&GdevhdrHdr, "/dev/eeprom", iDrvNum); /* 安裝設(shè)備 */ Gi2cDev = API_I2cDeviceCreate("/bus/i2c/1", "/dev/eeprom", DEVICE_ADDR, 0); return ERROR_NONE; }
模塊卸載實(shí)現(xiàn)如程序清單 3-8所示。
程序清單 3-8 模塊卸載
/********************************************************************************************************* ** 函數(shù)名稱: module_exit ** 功能描述: 驅(qū)動(dòng)卸載模塊 ** 輸 入 : NONE ** 輸 出 : NONE *********************************************************************************************************/ void module_exit (void) { printk("hello_module exit!\n"); API_IosDevDelete(&GdevhdrHdr); /* 刪除設(shè)備 */ API_I2cDeviceDelete(Gi2cDev); /* 刪除指定的 i2c 設(shè)備 */ return ; }
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。