一、概述
SemiDrive X9H 擁有不同的 domain 域,例如 AP,Safety,Secure 等等。對于 GPIO 資源,不同 domain 之間 gpio 控制器是不同的,本次主要是使用的 AP 使用的是 gpio4 控制器。除了GPIO 控制器在不同 domain 之間不同以外,pin 也是有各自不同的 domain 的,但是當它作為 gpio 使用時,可以通過設置掛靠到對應域下的 gpio 控制器上來在對應 domain 下使用。具體 pin 的 gpio 控制器的設置是通過 PC 工具 SDConfigTool 來進行。
本文主要是講述將 pin 作為 GPIO 使用,之后編寫字符設備驅動,測試。
二、適用環(huán)境
硬件:SemiDrive SD003_X9H REF_A03 DEMO Board
軟件:X9 PTG3.9
三、設備樹匹配
1、復用管腳為 GPIO
路徑:
/buildsystem/yocto/source/linux/arch/arm64/boot/dts/semidrive/x9_high_ref_native_serdes_nobt.dts
在上面 dts 文件的 &pinctrl 的 sdx9-evk 中添加管腳復用,主要是將 GPIO_C1 管腳復用成 GPIO。
pinctrl_gpio_learning: gpiogrp_learning {
kunlun,pins = <
X9_PINCTRL_GPIO_C1__GPIO_MUX2_IO1_1 0x00
>;
};
2、配置一個新節(jié)點
路徑:
/buildsystem/yocto/source/linux/arch/arm64/boot/dts/semidrive/x9_high_ref_native_serdes_nobt.dts
在上面 dts 文件里面的根節(jié)點 / 下新建一個節(jié)點,添加屬性,獲取 gpio 編號。
gpio_learning {
#address-cells = <1>;
#size-cells = <1>;
compatible = "gpioled_learning";
pinctrl-0 = <&pinctrl_gpio_learning>;
gpio = <&port4b 17 GPIO_ACTIVE_HIGH>;
status = "okay";
};
四、驅動編寫
1、模塊出/入口函數(shù)
其中 module_init 是驅動入口函數(shù),主要進行 platform 平臺驅動注冊,module_exit 是驅動出口函數(shù),主要是進行 platform 平臺驅動注銷;
其中 MODULE_LICENSE 主要是聲明模塊許可證,一般為 GPL。
/*設備入口函數(shù)*/
static int __init gpio_learning_init(void)
{
return platform_driver_register(&gpio_learning_driver);
}
/*設備出口函數(shù)*/
static void __exit gpio_learning_exit(void)
{
platform_driver_unregister(&gpio_learning_driver);
}
/*指定上面的入口和出口函數(shù)*/
module_init(gpio_learning_init);
module_exit(gpio_learning_exit);
MODULE_LICENSE("GPL"); //LICENSE 采用 GPL 協(xié)議
2、platform 平臺驅動結構體
主要是通過 match 函數(shù)和對應的設備樹里面節(jié)點匹配,只要 compatible 屬性匹配成功即可,匹配成功就執(zhí)行 probe 函數(shù)。
/*匹配列表*/
static const struct of_device_id gpio_learning_of_match[] = {
{ .compatible = "gpioled_learning" },
{ /*sentinel*/}
};
/*
* platform 平臺驅動結構體
*/
static struct platform_driver gpio_learning_driver ={
.driver = {
.name = "gpio_learning_device",
.of_match_table = gpio_learning_of_match,
},
.probe = gpio_learning_probe,
.remove = gpio_learning_remove,
};
3、probe 函數(shù)
主要是注冊字符設備,通過獲取設備樹的節(jié)點,獲取 gpio 屬性,從而獲得 gpio編號,之后請求使用該 gpio,設置 gpio 模式。
/*
* platform 驅動的 probe 函數(shù)
* 驅動與設備匹配成功以后此函數(shù)就會執(zhí)行
*/
static int gpio_learning_probe(struct platform_device *dev)
{
printk("led driver and device was matched!\r\n");
/* 1、設置設備號 */
if (gpiodev.major) { //如果定義了主設備號
gpiodev.devid = MKDEV(gpiodev.major, 0);//次設備號號默認 0,構建設備號
register_chrdev_region(gpiodev.devid, GPIODEV_CNT,GPIODEV_NAME);//注冊設備號
} else {
alloc_chrdev_region(&gpiodev.devid, 0, GPIODEV_CNT,GPIODEV_NAME);//動態(tài)申請設備號
gpiodev.major = MAJOR(gpiodev.devid);//獲取主設備號
}
/* 2、注冊設備 */
cdev_init(&gpiodev.cdev,&gpio_learning_fops);
cdev_add(&gpiodev.cdev, gpiodev.devid, GPIODEV_CNT);
/* 3、創(chuàng)建類 */
gpiodev.class = class_create(THIS_MODULE, GPIODEV_NAME);
if (IS_ERR(gpiodev.class)) {
return PTR_ERR(gpiodev.class);
}
/* 4、創(chuàng)建設備 */
gpiodev.device = device_create(gpiodev.class, NULL, gpiodev.devid,NULL, GPIODEV_NAME);
if (IS_ERR(gpiodev.device)) {
return PTR_ERR(gpiodev.device);
}
/* 5、初始化 IO */
gpiodev.node = of_find_node_by_path("/gpio_learning");
if (gpiodev.node == NULL){
printk("gpio_learning node nost find!\r\n");
return -EINVAL;
}
gpiodev.led0 = of_get_named_gpio(gpiodev.node, "gpio", 0);/*獲取要使用的 GPIO 編號 */
if (gpiodev.led0 < 0) {
printk("can't get gpio\r\n");
return -EINVAL;
}
printk("led0 = %d\r\n",gpiodev.led0);
int ret = gpio_request(gpiodev.led0, "led0");/*申請gpio管腳 */
if(ret == 0)
{
printk("gpio_request success\r\n");
}
else
{
printk("gpio_request fail\r\n");
return -1;
}
ret = gpio_direction_output(gpiodev.led0, 1); /*設置為輸出,默認高電平 */
if(ret == 0)
{
printk("gpio_direction_output success\r\n");
}
else
{
printk("gpio_direction_output fail\r\n");
return -1;
}
return 0;
}
4、設備函數(shù)操作結構體
主要是一個 open 和 write 的函數(shù)。
/*
* 設備操作函數(shù)結構體
*/
static struct file_operations gpio_learning_fops = {
.owner = THIS_MODULE,
.open = gpio_learning_open,
.write = gpio_learning_write,
};
5、設備操作 open 和 write函數(shù)
主要 open 函數(shù)是將 private_data 指向設備結構體。
主要 write 函數(shù)是接受用戶層傳來的數(shù)據(jù),之后根據(jù)數(shù)據(jù)設置 gpio 操作。
#define GPIODEV_CNT 1 /* 設備號長度 */
#define GPIODEV_NAME "gpio_learning" /* 設備名字 */
#define GPIOOFF 0
#define GPIOON 1
//設備結構體
struct gpiodev_dev{
dev_t devid; /* 設備號 */
struct cdev cdev; /* cdev */
struct class *class; /* 類 */
struct device *device; /* 設備 */
int major; /* 主設備號 */
struct device_node *node; /* 設備節(jié)點 */
int led0; /* LED 燈 GPIO 標號 */
};
struct gpiodev_dev gpiodev;
/*
* @description : LED 打開/關閉
* @param - sta : LEDON(0) 打開 LED,LEDOFF(1) 關閉 LED
* @return : 無
*/
void led0_switch(u8 sta)
{
if (sta == GPIOON )
{
gpio_set_value(gpiodev.led0, 1);
printk("gpio_set_value = 1!\r\n");
}
else if (sta == GPIOOFF)
{
gpio_set_value(gpiodev.led0, 0);
printk("gpio_set_value = 0!\r\n");
}
return 0;
}
/*
* @description : 打開設備
* @param – inode : 傳遞給驅動的 inode
* @param - filp : 設備文件,file 結構體有個叫做 private_data 的成員變量
* 一般在 open 的時候將 private_data 指向設備結構體。
* @return : 0 成功;其他 失敗
*/
static int gpio_learning_open(struct inode *inode,struct file *filp)
{
filp->private_data = &gpiodev; /* 設置私有數(shù)據(jù) */
return 0;
}
static ssize_t gpio_learning_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
int retvalue;
unsigned char databuf[2];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
printk("retvalue = %d\r\n",retvalue);
ledstat = databuf[0];
if (ledstat == GPIOON) {
printk("ledstat = 1\r\n");
led0_switch(GPIOON);
} else if (ledstat == GPIOOFF) {
printk("ledstat = 0\r\n");
led0_switch(GPIOOFF);
}
return 0;
}
五、Kernel 配置
1、Makefile 文件
obj-$(CONFIG_GPIO_LEARNING) += gpio_learning.o
2、Kconfig 文件
config GPIO_LEARNING
tristate "GPIO learning block support"
default m
help
This is enable gpio learning test
3、引用
在自己編寫文件的上一級目錄 Makefile 和 Kconfig 添加對應引用,并在對應 deconfig 配置。
Makefile 文件引用
obj-$(CONFIG_GPIO_LEARNING) += gpio_learning/
Kconfig 文件引用
source "drivers/gpio_learning/Kconfig"
deconfig 文件配置
路徑:
/buildsystem/yocto/source/linux/arch/arm64/configs/x9_ref_linux_defconfig
等于 m 表示編譯成 ko 文件,等于 y 表示編譯進內(nèi)核。
CONFIG_GPIO_LEARNING=m
六、APP 測試
主要是傳入三個參數(shù),一個是運行的 app,一個是對應的 /dev/xxx,一個是對 gpio 的操作(1/0),通過 /dev/xxx 打開對應驅動文件,通過 write 發(fā)送對應的操作。
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char databuf[2];
if(argc != 3){ //傳入三個參數(shù):運行的app /dev/xxx 操作
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打開 /dev/xxx 驅動文件 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]); /* 要執(zhí)行的操作:打開或關閉 */
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
retvalue = close(fd); /* 關閉文件 */
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
以上完成了 SemiDrive X9H GPIO 功能的實現(xiàn)。
接下來將會更新更多關于 SemiDrive X9H 的開發(fā)博文,如有相關技術問題,可在評論區(qū)留言。
七、參考文檔
《 SemiDrive_9_Series_GPIO使用手冊_Rev01.00.pdf 》
《 X9H處理器數(shù)據(jù)手冊_Rev04.00.pdf 》
《 SD003_X9H_REF_A03_SCH.pdf 》
《 X9_Processor_TRM_Rev00.07.pdf 》
-
控制器
+關注
關注
112文章
16419瀏覽量
178804 -
驅動
+關注
關注
12文章
1846瀏覽量
85424 -
GPIO
+關注
關注
16文章
1215瀏覽量
52232
發(fā)布評論請先 登錄
相關推薦
評論