標籤:
硬體平台:
1 主控:SMDK Exynos4412 POP S5M8767A
2 RFID模組:君盾集團提供的RC522模組
3 通訊介面:SPI
軟體平台:Android ICS & kernel version 3.0.15
一,使能主控端SPI
1 硬體使能:
從SMDK原理圖上可以看到SPI0與I2C共用,SPI1已經串連到其它裝置,SPI2未用,故這裡選用SPI2。
2 軟體使能:
SMDK Exynos4412 主控端已經配置好了SPI介面,使用時只需開啟宏CONFIG_S3C64XX_DEV_SPI即可。
開啟檔案:make menuconfigàDevice DriversàSPI supportàSamsung S3C64XX series type SPI.
編譯後產生zImage,燒錄進開發板。
二,測試主控端SPI
主控端SPI已經開啟,接下來可以用一個通用的SPI驅動來測試主控端SPI硬體是否能正常工作。
以模組的方式編譯:drivers/spi/spidev.c,產生spidev.ko,便是通用的裝置端SPI驅動程式。
編譯測試程式:Documentation/spi/spidev_test.c,先修改第32行: static const char *device = "/dev/spidev1.1"的裝置為“/dev/spidev2.0”,然後再以應用程式的方式來編譯,產生spidev_test,即為對應SPI的測試程式。
通過串口,賦予root許可權和系統可讀寫權限:
[email protected]:/ $ su root
[email protected]:/ # mount -o remount -rw /system
[ 391.423930] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
[email protected]:/ #
通過adb把spidev.ko和spidev_test push到開發板:
載入驅動:
[email protected]:/system # insmod spidev.ko
把MISO和MOSI短路,即自發自收,然後再執行測試程式:
如所示,說明能通過SPI收發資料;如果全部顯示為0,則說明SPI未正常工作。接下來可以放心地去調試我們的RC522模組了。
三,RC522裝置端驅動調試
上面的實驗已經證明了主控的SPI可以正常工作,接下來可以正式調試RC522了。――這裡假設你的rfid_rc522驅動已經寫好,現在只需要去調試――如果驅動沒有寫好,請看另一篇Blog。
1 開啟與開發板相關的檔案:arch/arm/mach-exynos/mach-smdk4x12.c
由於使用的spi2,故要修改board_info裡的modalias = “rfid_rc522”,與驅動裡的spi_drviver.name相匹配,否則probe函數不成功。
點擊(此處)摺疊或開啟
- 988 static struct spi_board_info spi2_board_info[] __initdata = {
- 989 {
- 990 .modalias = "rfid_rc522",
- 991 .platform_data = NULL,
- 992 .max_speed_hz = 10*1000*1000,
- 993 .bus_num = 2,
- 994 .chip_select = 0,
- 995 .mode = SPI_MODE_0,
- 996 .controller_data = &spi2_csi[0],
- 997 }
- 998 };
2 重新編譯核心,並燒錄到開發板。
3 編譯rc522驅動程式,並通過adb usb把產生的rfid_rc522.ko copy到開發板系統的/system目錄下,然後 insmod rfid_rc522.ko, 這樣驅動就以模組的形式載入進了核心系統。載入成功後,在/dev 目錄下就會有rfid_rc522_dev這個目錄。
四,應用程式
驅動中的write函數為:
rc522_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos);
使用者空間的應用程式write函數為:
write(rc522_fd, bufpw1, sizeof(bufpw1));
二者如何聯絡的呢?
其實應用程式中的write函數通過叫用作業系統中的核心函數sys_write(unsigned int fd, const char * buf, size_t count)來實現,而sys_write()函數又對驅動中的rc522_write()進行了封裝。
//摘自論壇開始
下面以字元裝置驅動來具體說明:
1,insmod驅動程式。驅動程式申請次裝置名稱和主裝置號,這些可以在/proc/devieces中獲得。
2,從/proc/devices中獲得主裝置號,並使用mknod命令建立裝置節點檔案。這是通過主裝置號將裝置節點檔案和裝置驅動程式聯絡在一起。裝置節點檔案中的file屬性中指明了驅動程式中fops方法實現的函數指標。
3,使用者程式使用open開啟裝置節點檔案,這時作業系統核心知道該驅動程式工作了,就調用fops方法中的open函數進行相應的工作。open方法一般返回的是檔案標示符,實際上並不是直接對它進行操作的,而是有作業系統的系統調用在背後工作。
4,當使用者使用write函數操作裝置檔案時,作業系統調用sys_write函數,該函數首先通過檔案標示符得到裝置節點檔案對應的inode指標和flip指標。inode指標中有裝置號資訊,能夠告訴作業系統應該使用哪一個裝置驅動程式,flip指標中有fops資訊,可以告訴作業系統相應的fops方法函數在那裡可以找到。
5,然後這時sys_write才會調用驅動程式中的write方法來對裝置進行寫的操作。
其中1-3都是在使用者空間進行的,4-5是在核心空間進行的。使用者的write函數和作業系統的write函數通過系統調用sys_write聯絡在了一起。
注意:
對於塊裝置來說,還存在寫的模式的問題,這應該是由GNU C庫來解決的,這裡不予討論,因為我沒有看過GNU C庫的原始碼。
//摘自論壇結束
應用程式源碼如下:
點擊(此處)摺疊或開啟
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <errno.h>
- #include <arpa/inet.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <math.h>
- static enum IO_CMD {
- READ_CARD = 0,
- CHANGE_PASSWD = 1,
- CHANGE_BLOCK = 3,
- SET_RW_TIME = 4,
- WRITE_CARD = 5,
- };
- int main(int argc, char** argv)
- {
- int rc522_fd;
- int i, read_num;
- char r[256];
- printf("test: rc522 %s %s\n", __DATE__, __TIME__);
-
- printf("test: before open rc522_fd\n");
- rc522_fd = open("/dev/rfid_rc522_dev", O_RDWR);
-
- printf("test: rc522_fd=%d\n", rc522_fd);
- if(rc522_fd == -1)
- {
- printf("test: Error Opening rc522\n");
- return(-1);
- }
- printf("test: wait 01\n");
- sleep(1); //wait
- printf("test: wait 02\n");
- /******* Never to open ********/
- #if 0
- // change password as:020202020202
- ioctl(rc522_fd, CHANGE_BLOCK, 0);//參數3:選第0塊
- ioctl(rc522_fd, CHANGE_PASSWD, 0);
- char bufpw1[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
-
- write(rc522_fd, bufpw1, sizeof(bufpw1));
- #endif
- ioctl(rc522_fd, CHANGE_BLOCK, 0);//參數3:選第0塊
- ioctl(rc522_fd, READ_CARD, 0);//參數3沒用
- for(i = 0; i < 3; i++) //讀三次卡號
- {
- read_num = read(rc522_fd, r, 0);
- printf("test: i=%d read_num=%d ", i, read_num);
- if(read_num > 0){
- printf("r=[0x%.2X]", r[0]);
- }
-
- printf("\n");
- sleep(1);
- }
-
- // write something to the card
- ioctl(rc522_fd, CHANGE_BLOCK, 1);//參數3:選第2塊
- ioctl(rc522_fd, WRITE_CARD, 1);
- printf("before write card!\n");
-
- char buf[11] = "186653803xx";
- if(write(rc522_fd, buf,sizeof(buf)))
- {
- printf("write error\n");
- }
- // read block[1], just writed with
- ioctl(rc522_fd, CHANGE_BLOCK, 1);//參數3:選第1塊
- ioctl(rc522_fd, READ_CARD, 0);//參數3沒用
- read_num = read(rc522_fd, r, 0);
- printf("read block[1]\n\n\n The number you just writed is: %s\n\n\n", r);
- printf("test: close rc522_fd\n");
- close(rc522_fd);
- printf("test: exit rc522_fd\n");
- return 0;
- }
應用程式編譯的Makefile 如下:
點擊(此處)摺疊或開啟
- # Comment/uncomment the following line to disable/enable debugging
- #DEBUG = y
- DEST_BIN_DIR= drivers/
- EXTRA_CFLAGS += -D_V3
- #TESTFLAGS = -D_V3
- # Add your debugging flag (or not) to CFLAGS
- ifeq ($(DEBUG),y)
- DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
- else
- DEBFLAGS = -O2
- endif
- EXTRA_CFLAGS += $(DEBFLAGS)
- EXTRA_CFLAGS += -I$(LDDINC)
- EXTRA_CFLAGS += -DREV_VERSION=$(REV_VERSION)
- LDFLAGS += --static
- all: test
- clean:
- rm -rf test_rc522
-
- cp:
- cp -f test_rc522 $(DEST_BIN_DIR)
- mv:
- mv -f test_rc522 $(DEST_BIN_DIR)
- test:
- arm-linux-gcc $(CFLAGS) $(LDFLAGS) -O2 test_rc522.c -o test_rc522
- depend .depend dep:
- $(CC) $(CFLAGS) -M *.c > .depend
- ifeq (.depend,$(wildcard .depend))
- include .depend
- endif
測試時,把卡靠近RC522的天線地區,即可正常讀到卡ID。
五,總結
本次調試比較順利,遇到幾個比較大的問題如下:
1 SMDK開發板SPI0通訊有問題,開始一直以為驅動的問題,也不知道應該如何測試開發板SPI介面是否OK,在網上找了一些資料後發現SPI驅動可以通過核心內建的驅動模組和應用程式進行測試。
2 應用程式(測試程式)無法在開發板系統上運行,原因是連結庫未設定成靜態。
3 RC522中的VCC供電需要3.3V,MOSI,CLK等TTL高電平也是3.3V。但4412主控的GPIO輸出的高電平全部是1.8V,故模組無法正常工作。由於是調試,故不可能加一個TTL電平轉換的IC了。後來試著把VCC調低到2.6V,結果模組可以正常工作了。
(轉載)
RC 522模組在LINUX平台調試筆記