Linux下的點燈實驗(暫存器)
阿新 • • 發佈:2021-12-23
1. 地址對映
裸機就是直接操作暫存器
Linux下也可以直接操作暫存器,但是不能直接對暫存器的實體地址進行操作,因為linux會使能MMU。
在使用時,必須找到實體地址的虛擬地址
#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype) { return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0)); }
獲得虛擬地址的函式,第一個引數是實體地址起始地址,第二個引數是大小,不僅可以地址轉換,也可以記憶體塊的地址轉換。
比如ioremap(0x010100101, 10), 返回的是虛擬地址的起始地址
解除安裝地址的時候,使用iounmap函式。
2. 程式碼
led.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #define LED_MAJOR 200 //主裝置號 #define LED_NAME "led" /* 暫存器的實體地址 */ #define CCM_CCGR1_BASE (0X020C406C) #define SW_MUX_GPIO1_IO03_BASE (0X020E0068) #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) #define GPIO1_DR_BASE (0X0209C000) #define GPIO1_GDIR_BASE (0X0209C004) /* 地址對映後的虛擬地址指標 */ //iomem是虛擬地址的型別 static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR; #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 */ /* LED燈開啟或者關閉 */ static void led_switch(u8 sta) { u32 val = 0; if(sta == LEDON){ val = readl(GPIO1_DR); val &= ~(1 << 3); writel(val, GPIO1_DR); } else if(sta == LEDOFF) { val = readl(GPIO1_DR); val |= (1 << 3); writel(val, GPIO1_DR); } } static int led_open(struct inode *inode, struct file *filp) { return 0; } static int led_release(struct inode *inode, struct file *filp) { return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int retValue; unsigned char dataBuf[1]; /* 1表示1位,開燈關燈 */ retValue = copy_from_user(dataBuf, buf, count); if(retValue < 0){ printk("kernel write failed! \r\n"); return -EFAULT; } /* 判斷開燈還是關燈 */ led_switch(dataBuf[0]); return 0; } /* 字元裝置操作集合 */ static const struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, }; /* 入口函式 */ static int __init led_init(void) { int ret = 0; unsigned int val = 0; /* 初始化LED燈 */ /* 1.地址對映 */ IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4); SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4); GPIO1_DR = ioremap(GPIO1_DR_BASE, 4); GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4); /* 2.初始化 */ val = readl(IMX6U_CCM_CCGR1); val &= ~(3 << 26); /* 先清楚以前的bit26 27 */ val |= 3 << 26; /* 26 27 置1 */ writel(val, IMX6U_CCM_CCGR1); /* 3、設定 GPIO1_IO03 的複用功能,將其複用為 * GPIO1_IO03,最後設定 IO 屬性。 */ writel(0x5, SW_MUX_GPIO1_IO03); /* 設定複用 */ writel(0x10B0, SW_PAD_GPIO1_IO03); /* 設定電氣屬性 */ /* 4、設定 GPIO1_IO03 為輸出功能 */ val = readl(GPIO1_GDIR); val &= ~(1 << 3); val |= 1 << 3; writel(val, GPIO1_GDIR); /* 5、預設開啟 LED */ val = readl(GPIO1_DR); val &= ~(1 << 3); writel(val, GPIO1_DR); /* 6.註冊裝置驅動 */ ret = register_chrdev(LED_MAJOR, LED_NAME, &led_fops); if(ret < 0) { printk("register chrdev failed! \r\n"); return -EIO; //ERROR IO的意思,負數是一般錯誤都是負數 } printk("led module init \r\n"); return 0; } /* 出口函式 */ static void __exit led_exit(void) { /* 解除安裝之前記得關燈 */ unsigned int val = 0; val = readl(GPIO1_DR); val |= (1 << 3); writel(val, GPIO1_DR); /* 解除安裝虛擬地址 */ iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO03); iounmap(SW_PAD_GPIO1_IO03); iounmap(GPIO1_DR); iounmap(GPIO1_GDIR); unregister_chrdev(LED_MAJOR, LED_NAME); printk("led module exit \r\n"); } /* 註冊驅動的載入和解除安裝 */ module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Shao Zheming");
ledApp.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> /* * argc: 應用程式引數個數 * argv[]: 引數是什麼,具體的引數,說明引數是字串的形式 * .chrdevbaseApp <filename> <0:1> 0表示關燈,1表示開燈 * .chrdevbaseApp /dev/led 0 關燈 * .chrdevbaseApp /dev/led 1 開燈 * */ #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 */ int main(int argc, char *argv[]) { if(argc != 3) { printf("Error Usage!\r\n"); return -1; } char *filename; int fd, retValue; unsigned char dataBuf[1]; filename = argv[1]; fd = open(filename, O_RDWR); if(fd < 0) { printf("file %s open failed! \r\n", filename); return -1; } dataBuf[0] = atoi(argv[2]); retValue = write(fd, dataBuf, sizeof(dataBuf)); if(retValue < 0){ printf("Led Control Failed \r\n"); close(fd); return -1; } close(fd); return 0; }
3. 注意事項
- uboot平常是正常的,但是突然不能下載的原因?
虛擬機器的Ubuntu的ip地址或者開發板的ip地址衝突,所以需要保證整個網段內ip地址唯一-
4. 測試
- depmod //第一次載入驅動的時候需要執行此命令
- 先將led.ko和ledApp拷貝到 /home/szm/linux/mfs/rootfs/lib/modules/4.1.15中
- 在Ubuntu上 cd /lib/modules/4.1.15
- 載入驅動 modprobe led.ko
- 檢視是否存在這個驅動 cat /proc/devices
- 存在的話:mkmod /dev/led c 200 0
- 測試燈開啟 ./ledApp /dev/led 1
- 測試完畢後 rmmod led.ko