本机使用的是正点原子的IMX6ULL开发板
# 前置知识
IMX6ULL GPIO控制框图:
GPIO控制代码大概分为几个流程:开启时钟、设置IO复用、设置IO属性、配置IO方向、设置IO输出电平,下面以IMX6ULL为例,
1. 开启时钟
参考资料:芯片手册《Chapter 18: Clock Controller Module (CCM)》
关于CCM_CCGRx主要是由如下介绍:
00:该 GPIO 模块全程被关闭
01:该 GPIO 模块在 CPU run mode 情况下是使能的;在 WAIT 或 STOP模式下,关闭
10:保留
**11:**该 GPIO 模块全程使能
IMX6ULL 使用的LED引脚为GPIO1_IO03,GPIO1时钟控制:
2. 设置IO复用
参考资料:芯片手册《Chapter 32: IOMUX Controller (IOMUXC)》
选择功能:
a) IOMUXC_SW_MUX_CTL_PAD_<PADNAME> : Mux pad xxx,选择某个 pad 的功能
b) IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>: Mux grp xxx,选择某组引脚的功能
比如我们需要选择的LED灯引脚为GPIO1_IO03,那么选择对应的寄存器,设置复用模式为ALT5即可:
3. 设置IO属性
参考资料:芯片手册《Chapter 32: IOMUX Controller (IOMUXC)》
设置IO属性等参数:
a) IOMUXC_SW_PAD_CTL_PAD_<PAD_NAME>: pad pad xxx,设置某个 pad 的参数
b) IOMUXC_SW_PAD_CTL_GRP_<GROUP NAME>: pad grp xxx,设置某组引脚的参数
比如我们需要选择的LED灯引脚为GPIO1_IO03,那么选择对应的寄存器:
4. 设置IO方向
参考资料:芯片手册《Chapter Chapter 28 General Purpose Input/Output (GPIO)》
GPIO的框图如下:
GPIO模块一共有8个寄存器:
- 设置方向寄存器:GPIO_GDIR
- 设置输出电平寄存器:GPIO_DR
- 读取输入电平寄存器:GPIO_PSR
- 中断控制寄存器:GPIO_ICR1, GPIO_ICR2
- 边沿选择寄存器:GPIO_EDGE_SEL
- 中断掩码寄存器:GPIO_IMR
- 中断状态寄存器:GPIO_ISR
我们需要控制LED灯打开或者关闭,那么就需要以下步骤:
- 设置GPIO为输出
- 控制GPIO输出
# 驱动程序
1. 驱动代码
cpp
#include <linux/module.h>
#include <linux/major.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h> /* guess what */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h> /* For put_user and get_user */
#include "atk_led_op.h"
struct pri_led_TypeDef
{
char drv_name[50]; /* 驱动名称 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devt; /* 设备号 */
struct device *device; /* 设备 */
char device_name[50]; /* 设备名称 */
struct class *class; /* 类 */
char class_name[50]; /* 类名称 */
};
static struct pri_led_TypeDef pri_led = {
.drv_name = "led_drv",
.major = 0,
.minor = 0,
.devt = 0,
.device = NULL,
.device_name = "led_dev",
.class = NULL,
.class_name = "led_class",
};
struct led_operations *atk_led;
static int led_open(struct inode *inode, struct file *file)
{
atk_led->init();
return 0;
}
static int led_release(struct inode *inode, struct file *file)
{
atk_led->deInit();
return 0;
}
static ssize_t led_write(struct file *file, const char __user *buff, size_t size, loff_t *ppos)
{
int err;
unsigned char data[1];
err = copy_from_user(data, buff, 1);
if(data[0] == 1)
{
atk_led->ctrl(1);
}
else
{
atk_led->ctrl(0);
}
return 1;
}
static const struct file_operations led_op = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
};
static int __init led_init(void)
{
int err;
printk("led_init\r\n");
pri_led.major = register_chrdev(0, pri_led.drv_name, &led_op);
pri_led.devt = MKDEV(pri_led.major, pri_led.minor);
pri_led.class = class_create(THIS_MODULE, pri_led.class_name);
if(IS_ERR(pri_led.class))
{
printk("class_create error\r\n");
err = PTR_ERR(pri_led.class);
goto err_class_create_out;
}
pri_led.device = device_create(pri_led.class, NULL, pri_led.devt, NULL, pri_led.device_name);
if(IS_ERR(pri_led.device))
{
printk("device_create error\r\n");
err = PTR_ERR(pri_led.device);
goto err_device_create_out;
}
atk_led = atk_led_op_register();
return 0;
err_device_create_out:
class_destroy(pri_led.class);
err_class_create_out:
unregister_chrdev(pri_led.major, pri_led.drv_name);
return err;
}
static void __exit led_exit(void)
{
printk("led_exit\r\n");
device_destroy(pri_led.class, pri_led.devt);
class_destroy(pri_led.class);
unregister_chrdev(pri_led.major, pri_led.drv_name);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
2. 板级驱动代码
cpp
#ifndef __ATK_LED_OP_H__
#define __ATK_LED_OP_H__
#include <linux/io.h>
#include <linux/module.h>
#include <linux/major.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h> /* guess what */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h> /* For put_user and get_user */
struct led_operations
{
void (*init)(void);
void (*deInit)(void);
int (*ctrl)(int status);
};
struct led_operations *atk_led_op_register(void);
#endif /* __ATK_LED_CTRL_H__ */
cpp
#include "atk_led_op.h"
/**
* 1. 查看原理图,LED0 --> GPIO1_IO03
* 2. 开启时钟,CCM_CCGR1->CG13 //Address: 20C_4000h base + 6Ch offset = 20C_406Ch
* 3. 设置IO复用,IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 //Address: 20E_0000h base + 68h offset = 20E_0068h
* 4. 设置IO属性,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 //Address: 20E_0000h base + 2F4h offset = 20E_02F4h
* 5. GPIO1寄存器: GPIO1_DR //Address: 209_C000
* GPIO1_GDIR //Address: 209_C004
* GPIO1_PSR //Address: 209_C008
* GPIO1_ICR1 //Address: 209_C00C
* GPIO1_ICR2 //Address: 209_C010
* GPIO1_IMR //Address: 209_C014
* GPIO1_ISR //Address: 209_C018
* GPIO1_EDGE_SEL //Address: 209_C01C
*/
#define CCM_CCGR1_ADDR 0x20C406C
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR 0x20E0068
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR 0x20E02F4
#define GPIO1_BASE_ADDR 0x209C000
typedef struct
{
volatile uint32_t GPIO_DR;
volatile uint32_t GPIO_GDIR;
volatile uint32_t GPIO_PSR;
volatile uint32_t GPIO_ICR1;
volatile uint32_t GPIO_ICR2;
volatile uint32_t GPIO_IMR;
volatile uint32_t GPIO_ISR;
volatile uint32_t GPIO_EDGE_SEL;
}GPIO_TypeDef;
static GPIO_TypeDef *GPIO1;
static volatile uint32_t *CCM_CCGR1;
static volatile uint32_t *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03;
static volatile uint32_t *IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03;
void atk_led_init(void)
{
uint32_t val;
/*1. 地址映射 */
GPIO1 = ioremap(GPIO1_BASE_ADDR, sizeof(GPIO_TypeDef));
CCM_CCGR1 = ioremap(CCM_CCGR1_ADDR, 4);
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = ioremap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR, 4);
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = ioremap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR, 4);
/* 2. 使能GPIO1时钟 */
*CCM_CCGR1 |= (3 << 26);
/* 3. 设置IO复用 */
val = *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03;
val &= ~(0x0F);
val |= 5;
*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = val;
/* 4. 设置IO属性 */
/* 5. 设置IO方向,设置GPIO1_IO03为输出 */
GPIO1->GPIO_GDIR |= (1 << 3);
}
void atk_led_deInit(void)
{
/* 取消地址映射 */
iounmap(GPIO1);
iounmap(CCM_CCGR1);
iounmap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03);
iounmap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03);
}
int atk_led_ctrl(int status)
{
if(status == 1)
{
GPIO1->GPIO_DR &= ~(1<<3);
}
else
{
GPIO1->GPIO_DR |= (1<<3);
}
return 1;
}
static struct led_operations led_op = {
.init = atk_led_init,
.deInit = atk_led_deInit,
.ctrl = atk_led_ctrl,
};
struct led_operations *atk_led_op_register(void)
{
return &led_op;
}
3. 测试APP
cpp
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/*
* ./test_app /dev/test_drv on/off
*/
int main(int argc, char **argv)
{
int fd;
unsigned char buff[1];
int len;
/* 1. 判断输入参数 */
if(argc < 3)
{
printf("Usage: %s on/off\n", argv[0]);
printf(" %s -r\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR);
if(fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
/* 3. 读写文件 */
if((strcmp(argv[2], "on") == 0))
{
buff[0] = 1;
write(fd, buff, 1);
}
else
{
buff[0] = 0;
write(fd, buff, 1);
}
close(fd);
return 0;
}