点亮led
1.看原理图,确定控制led的引脚
2.看芯片手册,确定如何设置/控制引脚
3.写程序,通过程序去控制引脚的状态
1. 看原理图 确定引脚
如图,imx6ull开发板led2的引脚为GPIO5_3,左边连接3.3V,所以我们只需要将右边的GPIO5_3输出低电平即可点亮led2。
2. 阅读芯片手册
CCM: Clock Controller Module (时钟控制模块)
IOMUXC : IOMUX Controller,IO复用控制器
GPIO: General-purpose input/output,通用的输入输出口
目的:查找相关寄存器,设置引脚输出低电平
步骤:
1> 首先我们要设置CCM里面的CCGR1寄存器的CG15位使能GPIO5;
2> 使能之后要设置GPIO5_3的引脚功能,来自于GPIO模块,需要设置IOMUXC里面的寄存器来选择GPIO5_3用于GPIO功能(IOMUXC里面有俩个寄存器:SW_MUX_CTL_PAD 用来设置选择引脚功能;SW_PAD_CTL_PAD 用来设置引脚是否上拉下拉电阻等等);
3> 设置GPIO.GDIR寄存器,将GPIO5_3设置为输出模式;
4> 设置GPIO.DR寄存器,将GPIO5_3为低电平。
2.1 设置CCM
设置CCM里面某一个寄存器,使能GPIO5,设置哪一个?看芯片手册。
如图,设置CCM里面的CCGR1寄存器的CG15位,设置为11(默认值就是11)即可使能GPIO5。
2.2 设置IOMUXC
根据原理图led2所连接的引脚SNVS_TAMPER3找到对应的寄存器。
如图,将MUX_MODE设置为101即可设置为GPIO功能。
//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址: 0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap( 0x02290000 + 0x14, 4);
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;//先把后四位清零
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;//把后三位设置为101
这里用到ioremap是因为驱动程序不可以直接使用物理地址,需要用ioremap函数将物理地址映射为虚拟地址,用这些虚拟地址去设置寄存器。
2.3 设置GPIO.GDIR
把引脚设置为GPIO模式之后,还需要设置引脚为输出模式
如图,把第三位设置为1,引脚即为输出模式。
//GPIO5_GDIR 地址是:0x020AC004
static volatile unsigned int *GPIO5_GDIR;
GPIO5_GDIR = ioremap( 0x020AC004, 4);
*GPIO5_GDIR |= (1<<3);//第三位置一,设置为输出模式
2.4 设置GPIO.DR
设置为输出模式之后,把引脚状态设置为低电平即可点亮led。
//GPIO5_DR 地址是:0x020AC000
static volatile unsigned int *GPIO5_DR;
GPIO5_DR= ioremap( 0x020AC000, 4);
*GPIO5_DR &= ~(1<<3);//拉低引脚,点亮led
3. 完成代码
3.1 驱动代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
static int major;
static struct class *local_led_class;
//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址: 0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
//GPIO5_GDIR 地址是:0x020AC004
static volatile unsigned int *GPIO5_GDIR;
//GPIO5_DR 地址是:0x020AC000
static volatile unsigned int *GPIO5_DR;
static ssize_t local_led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{
char val;
int err;
/* copy_from_user: 从应用程序里面得到数据 */
err = copy_from_user(&val,buf,1);
/* 去设置寄存器:输出高低电平 */
if(val){
/* 点亮 */
*GPIO5_DR &= ~(1<<3);
}
else{
/* 熄灭 */
*GPIO5_DR |= (1<<3);
}
return 1;
}
static int local_led_open(struct inode *inode, struct file *filp)
{
/*使能gpio模块
配置引脚为gpio功能
配置gpio为输出模式*/
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;//清掉最后的四位
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;//或上101,即可将后三位设置为101
*GPIO5_GDIR |= (1<<3);
return 0;
}
static struct file_operations local_led_fops= {
.owner = THIS_MODULE,
.write = local_led_write,
.open = local_led_open,
};
/* 入口函数 */
static int __init local_led_init(void)
{
printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
major = register_chrdev(0, "local_led", &local_led_fops);
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap( 0x02290000 + 0x14, 4);
//GPIO5_GDIR 地址是:0x020AC004
GPIO5_GDIR = ioremap( 0x020AC004, 4);
//GPIO5_DR 地址是:0x020AC000
GPIO5_DR= ioremap( 0x020AC000, 4);
local_led_class = class_create(THIS_MODULE, "local_led");
device_create(local_led_class, NULL, MKDEV(major, 0), NULL, "local_led");//系统会创建名为local_led的设备结点
return 0;
}
static void __exit local_led_exit(void)
{
device_destroy(local_led_class, MKDEV(major, 0));
class_destroy(local_led_class);
unregister_chrdev(major, "local_led1");
}
module_init(local_led_init);
module_exit(local_led_exit);
MODULE_LICENSE("Dual BSD/GPL");
3.2 测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,char **argv)
{
int fd;
char status = 0;
if(argc != 3){
printf("Usage : %s </dev> <off/on>\n",argv[0]);
printf(" eg : %s </dev> on\n",argv[0]);
printf(" eg : %s </dev> off\n",argv[0]);
return -1;
}
fd = open(argv[1],O_RDWR);
if(fd<0){
printf("can not open %s\n",argv[0]);
}
if(strcmp(argv[2],"on")==0){
status = 1;
}
write(fd,&status,1);
return 0;
}
编译代码拷贝至开发板,加载驱动,测试即可成功。