編輯:關於Android編程
MSM8909+Android5.1.1SPI驅動開發(PSAM部分)
1. PSAM部分的硬件設計
圖1
CS 片選信號
SCK 時鐘信號
MISO 主設備的數據輸入、從設備的數據輸出腳
MOSI 主設備的數據輸出、從設備的數據輸入腳
2. PSAM部分軟件設計
圖2
(1) PSAM應用
這部分只需要調用API函數即可。
(2) PSAM API
API的主要工作就是給M0上電、設置SPI讀寫模式、設置SPI讀寫的每字節的位數、SPI的工作頻率等。
(3) SPI驅動
接收到API設置SPI的參數後做相應的工作。
(4) M0固件
和TDA8007的主要工作由M0固件完成,包括協議的等等。
3. 組件配置
kernelarcharmconfigsmsm8909-1gb-CB03-perf_defconfig配置文件,確保下面選項設置如下:
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m,後來給改為y
如果是user版本就采用此文件,如果是eng文件就改msm8909-1gb-CB03_defconfig文件。
4. 設備樹配置
PSAM部分設備樹節點的設置層次
圖3
其中綠色矩形框部分是PSAM部分需要修改。
4.1 kernelarcharmootdtsqcommsm8909-cb03.dtsi增加SPI2控制器設備節點
aliases { /* smdtty devices */ smd1= &smdtty_apps_fm; smd2= &smdtty_apps_riva_bt_acl; smd3= &smdtty_apps_riva_bt_cmd; smd5= &smdtty_apps_riva_ant_cmd; smd6= &smdtty_apps_riva_ant_data; smd7= &smdtty_data1; smd8= &smdtty_data4; smd11= &smdtty_data11; smd21= &smdtty_data21; smd36= &smdtty_loopback; sdhc1= &sdhc_1; /* SDC1 eMMC slot */ sdhc2= &sdhc_2; /* SDC2 SD card slot */ //spi0= &spi_0; /* SPI0 controller device */ spi2 = &spi_2; /* SPI2 controller device */ i2c5 = &i2c_5; /* I2c5 cntroller device */ //i2c3= &i2c_3; /* I2C3 controller */ i2c1= &i2c_1; /* I2C1 controller */ i2c2= &i2c_2; /* I2C2 NFC qup2 device */ i2c4= &i2c_4; /* I2C4 controller device */ i2c6= &i2c_6; /* I2c6 cntroller device */ };
增加SPI2控制器設備節點信息
spi_2: spi@78b7000 { /* BLSP1 QUP3 */ compatible =qcom,spi-qup-v2; #address-cells = <1>; #size-cells = <0>; reg-names =spi_physical, spi_bam_physical; reg = <0x78b7000 0x600>, <0x78840000x23000>; interrupt-names =spi_irq, spi_bam_irq; interrupts = <0 97 0>,<0 238 0>; spi-max-frequency =<19200000>; pinctrl-names =spi_default, spi_sleep; pinctrl-0 =<&spi2_default &spi2_cs0_active>; pinctrl-1 = <&spi2_sleep&spi2_cs0_sleep>; clocks = <&clock_gccclk_gcc_blsp1_ahb_clk>, <&clock_gccclk_gcc_blsp1_qup3_spi_apps_clk>; clock-names =iface_clk, core_clk; qcom,infinite-mode = <0>; qcom,use-bam; qcom,use-pinctrl; qcom,ver-reg-exists; qcom,bam-consumer-pipe-index =<8>; qcom,bam-producer-pipe-index =<9>; qcom,master-id = <86>; };
這裡說明下SPI2的2指SPI控制器對應的總線號,對應spi_maste結構體的成員bus_num。
4.2 kernelarcharmootdtsqcommsm8909-pinctrl-cb03.dtsi增加SP2控制器引腳控制設置。
Pin控制的文檔可參考/kernel/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
spi2_active { /* MOSI, MISO, CLK */ qcom,pins = <&gp0>, <&gp 1>, <&gp 3>; qcom,num-grp-pins =<3>; qcom,pin-func =<1>; label =spi2-active; /* active state */ spi2_default:spi2_default { drive-strength= <12>; /* 12 MA */ bias-disable =<0>; /* No PULL */ }; }; spi2_suspend { /* MOSI, MISO, CLK */ qcom,pins = <&gp0>, <&gp 1>, <&gp 3>; qcom,num-grp-pins =<3>; qcom,pin-func =<0>; label =spi2-suspend; /* suspended state */ spi2_sleep: spi2_sleep{ drive-strength= <2>; /* 2 MA */ bias-pull-down; /* pull down */ }; }; spi2_cs0_active { /* CS */ qcom,pins = <&gp2>; qcom,num-grp-pins =<1>; qcom,pin-func =<1>; label =spi2-cs0-active; spi2_cs0_active:cs0_active { drive-strength= <2>; bias-disable =<0>; }; }; spi2_cs0_suspend { /* CS */ qcom,pins = <&gp2>; qcom,num-grp-pins =<1>; qcom,pin-func =<0>; label =spi2-cs0-suspend; spi2_cs0_sleep:cs0_sleep { drive-strength= <2>; bias-disable =<0>; }; };
4.3 kernelarcharmootdtsqcommsm8909-qrd-skue-cb03.dtsi增加SPI2控制器掛載的SPI從設備節點信息
spi@78b7000 { spidev@0{ compatible= nxp,lpc1114;//掛載的是NXP廠家的lpc1114設備 reg= <0>; spi-max-frequency= <1000000>; qcom,psam_en_gpio= <&msm_gpio 23 0>; }; };
圖4
這裡CS_N的N為什麼是0呢?再來看下圖:
圖5
我們可知BSP1~3都可以通過擴展的CS1、CS2和CS3來片選SPI從設備,當然還有CS0,也就是說BSP1~3對應的SPI0~SPI2控制器,每個都可以支持多達4個SPI從設備,BSP4~6只能掛接1個SPI從設備,只能通過CS0來片選。
5. 驅動代碼控制
5.1 PSAM_EN控制
新建PSAM電源控制的結構體
struct psam_control_data { /*system */ structspi_device *client; /*dtsi */ intpsam_en_gpio; };
在spidev_probe()函數中默認初始化PSAM_EN為低電平,控制代碼如下:
static bool parse_psam_control_dtsi(structdevice *dev, struct psam_control_data *psam_data) { //enumof_gpio_flags dummy; structdevice_node *node = dev->of_node; intret; psam_data->psam_en_gpio= of_get_named_gpio_flags(node, qcom,psam_en_gpio,0, NULL); if(psam_data->psam_en_gpio < 0) { returnERR_PTR(psam_data->psam_en_gpio); } else{ ret= gpio_request(psam_data->psam_en_gpio, psam_en_gpio); if(ret < 0){ pr_err(Failedto request psam_en_gpio, ERRNO:%d, ret); gotofree_gpio; } gpio_direction_output(psam_data->psam_en_gpio,0); } free_gpio: gpio_free(psam_data->psam_en_gpio); returntrue; }
在PSAM應用程序打開的時候,應用層通過ioctl(fd, SPI_IOC_SPI_IOC_ENABLE, &sam_enable)來控制上電,對應調用spidev.c下的spidev_ioctl(),增加case
case SPI_IOC_SPI_IOC_ENABLE: retval= __get_user(tmp, (u8 __user *)arg); if(retval == 0) { psam_power_control(tmp); } break;
#define SPI_IOC_SPI_IOC_ENABLE _IOW(SPI_IOC_MAGIC, 5, __u32)psam_power_control()函數的源代碼如下:
static bool psam_power_control( bool on) { intret; psam_control->psam_en_gpio= of_get_named_gpio_flags(psam_control->client->dev.of_node, qcom,psam_en_gpio,0, NULL); if(psam_control->psam_en_gpio < 0) returnERR_PTR(psam_control->psam_en_gpio); else{ ret= gpio_request(psam_control->psam_en_gpio, psam_en_gpio); if(ret < 0){ pr_err(Failedto request psam_en_gpio, ERRNO:%d, ret); gotofree_gpio; } gpio_direction_output(psam_control->psam_en_gpio,on); } free_gpio: gpio_free(psam_control->psam_en_gpio); returnret; }
同時參考2.3部分
static const struct of_device_idspidev_dt_ids[] = { //{.compatible = rohm,dh2228fv }, {.compatible = nxp,lpc1114 }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); static struct spi_driver spidev_spi_driver= { .driver= { .name= spidev, .owner= THIS_MODULE, .of_match_table= of_match_ptr(spidev_dt_ids), }, .probe= spidev_probe, .remove= spidev_remove, /*NOTE: suspend/resume methods are notnecessary here. * We don't do anything except pass therequests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handlesthe rest. */ };
5.3 CPOL和CPHA極性設置
我們用CPOL表示時鐘信號的初始電平的狀態,CPOL為0表示時鐘信號初始狀態為低電平,為1表示時鐘信號的初始電平是高電平。另外,我們用CPHA來表示在那個時鐘沿采樣數據,CPHA為0表示在首個時鐘變化沿采樣數據,而CPHA為1則表示要在第二個時鐘變化沿來采樣數據。內核用CPOL和CPHA的組合來表示當前SPI需要的工作模式:
CPOL=0,CPHA=1 模式0
CPOL=0,CPHA=1 模式1
CPOL=1,CPHA=0 模式2
CPOL=1,CPHA=1 模式3
我們這裡SPI從設備CPOL和CPHA采用的是模式1,所以我們SPI控制器也采用模式1。
module_init(spidev_init);
spidev_init()相關代碼如下:
if (busnum != -1 && chipselect !=-1) { structspi_board_info chip = { .modalias = spidev, .mode = spimode, .bus_num = busnum, .chip_select = chipselect, .max_speed_hz = maxspeed, }; structspi_master *master; master= spi_busnum_to_master(busnum); if(!master) { status= -ENODEV; gotoerror_busnum; } /*We create a virtual device that will sit on the bus */ spi= spi_new_device(master, &chip);
5.4
6. SPI測試代碼
在kernelDocumentationspi文件夾下就是SPI測試程序,其中spidev_test.c是用於測試自發自收的。我在systemextras下新建spi文件夾,並把spidev_test.c拷貝到spi文件夾下,並創建一個Android.mk文件,內容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := spidev_test LOCAL_SRC_FILES := spidev_test.c LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE)
然後進入此spi目錄下用mm命令編譯生成的可執行文件spidev_test在out argetproductmsm8909symbolssystemin目錄下。然後我們可以通過adb push命令把此文件拷貝到設備某個目錄下用./spidev_test執行,如果提示權限不夠,就用chmod 777 spidev_test命令。
用此程序可以測試spi設備驅動是否正常,但是否能夠正常驅動SPI從設備還需要根據具體的從設備來增加對應的控制。
7. 遇到的問題及解決
7.1 /sys/class/spi_master下找不到spi2
把kernelarcharmootdtsqcommsm8909-cb03.dtsi的i2c3 = &i2c_3注釋掉就可以看到了
aliases { … //spi0 = &spi_0; /* SPI0 controllerdevice */ spi2= &spi_2; /* SPI0 controller device */ i2c5 = &i2c_5; /* I2c5 cntroller device */ //i2c3 = &i2c_3; /* I2C3 controller */ i2c1= &i2c_1; /* I2C1 controller */ i2c2= &i2c_2; /* I2C2 NFC qup2 device */ i2c4= &i2c_4; /* I2C4 controller device */ i2c6= &i2c_6; /* I2c6 cntroller device */ };
7.2 /sys/class/spidev下看不到SPI2控制器下掛載的SPI從設備
(1) Spidev.c下修改
static const struct of_device_idspidev_dt_ids[] = { //{.compatible = rohm,dh2228fv }, {.compatible = nxp,lpc1114 }, {}, };
(2) kernelarcharmootdtsqcommsm8909-cb03.dtsi的spi_2: spi@78b7000節點下增加從設備節點
spi@78b7000 { spidev@0{ compatible= nxp,lpc1114; reg= <0>; spi-max-frequency= <1000000>; qcom,psam_en_gpio= <&msm_gpio 23 0>; }; };
7.3 應用層open()設備/dev/spidev2.0失敗
在systemcore ootdir init_CB03.rc增加下面的內容來修改權限
chmod 0666 /dev/spidev2.0
7.4 應用層用write()和read()測試自發自收失敗
用系統自帶的spidev_test采用的ioctl方式測試自發自收可以,但用write()寫返回值卻是0,正確的應該是返回我們實際寫入的字節數,目前原因不知道,相關的帖子:http://bbs.csdn.net/topics/391858635?page=1#post-400571674。
沒辦法我們PSAM的API層只能改用ioctl的方式就可以了。
7.5 SPI工作頻率過低
通過PSAM的API我設置SPI控制器工作的頻率為200kHZ,SPI控制器的spi_qsd提示頻率過低,改為1MHZ就可以了。
7.6 TDA8007給PSAM卡上電失敗
用我們實際的PSAM程序測試,根據調試信息可知選卡槽命令正常,但是給PSAM卡供5V電的時候一直沒有數據回來,後來查明是
圖6
把SW1的下拉改為上拉到3.3V就可以了。
使用SlidingTabLayout需要准備2個類,分別是 SlidingTabLayout,與SlidingTabStrip,,放進項目中時只用修改下包名即可。效果制作
使用ViewStub動態加載布局,避免一些不經常的視圖長期握住引用: ViewStub的一些特點: 1. ViewStub只能Inflate一次,之後ViewStub對象
前言:protobuf是google的一個開源項目,主要的用途是:1.數據存儲(序列化和反序列化),這個功能類似xml和json等;2.制作網絡通信協議;一、資源下載:1
接上篇Android 開發第五彈:簡易時鐘(鬧鐘) ,這次是一個時鐘類應用,目前依舊是主要的功能,長得還是很挫。當然了,核心功能是有的……時鐘