軟件版本:Vivado2021.1
操作系統(tǒng):WIN10 64bit、Ubuntu18.04
硬件平臺(tái):ZYNQ UltraScale
1.1系統(tǒng)框圖
在 PS 端接有兩個(gè) LED 燈,這些燈接在了 MIO 上,而 MIO 可以復(fù)用成為 GPIO,因此控制 GPIO 其實(shí)就是控制了 LED 等器件。本文通過讀寫寄存器來實(shí)現(xiàn)對(duì) GPIO 的控制。
1.2介紹
1.2.1寄存器查詢手冊(cè)
??使用 GPIO 需要設(shè)置兩個(gè)寄存器,一個(gè)是設(shè)置 GPIO 的管腳復(fù)用的 IOU_SLCR Module 寄存器,一個(gè)是設(shè)置GPIO 的管腳功能的 GPIO Module 寄存器。查詢寄存器地址需要用到一個(gè)官方文檔,編號(hào)的 ug1087。Xilinx 官方網(wǎng)站:https://docs.xilinx.com/r/en-US/ug1087-zynq-ultrascale-registers/MIO_PIN_41-IOU_SLCR-Register。
1.2.2物理地址與虛擬地址
??提到寄存器,那就不得不說說地址了。寄存器作為系統(tǒng)中存儲(chǔ)數(shù)據(jù)的器件,每個(gè)數(shù)據(jù)都有其自身的物理地址。而與物理地址相對(duì)的則是 Linux 系統(tǒng)下的虛擬地址。在裸機(jī)等通常程序里,是不存在虛擬地址一說的,對(duì)地址的操作就是對(duì)物理地址的操作。但是在 Linux 下,對(duì)物理地址直接訪問是行不通的,需要通過虛擬地址間接訪問,因此所有對(duì)寄存器的操作都需要通過地址映射。
??之所以要使用虛擬地址是因?yàn)橐郧暗碾娔X內(nèi)存非常小,程序也是非常小,因此靠手動(dòng)便可以管理內(nèi)存。但是日新月異,今天的程序已經(jīng)非常龐大了,雖然內(nèi)存也在飛速發(fā)展,但是無法跟上日益臃腫的應(yīng)用程序。眾所周知要想運(yùn)行一個(gè)程序,就必須將其完整地讀入內(nèi)存,如果程序本身就大于內(nèi)存,那除了加內(nèi)存可以運(yùn)行程序,那剩下的唯一辦法就是使用虛擬地址了。
虛擬地址可以模擬出幾倍于物理地址的內(nèi)存空間,當(dāng)程序運(yùn)行時(shí),被運(yùn)行的部分讀入內(nèi)存,其他部分依舊存放于外存。物理地址于虛擬地址的關(guān)系如下圖。
??虛擬內(nèi)存與物理內(nèi)存是對(duì)應(yīng)的,但是沒有順序。這個(gè)順序是由 MMU 管理的,MMU 全稱 Memory Management Unit,即內(nèi)存管理單元。它的功能包括虛擬地址到物理地址的轉(zhuǎn)換(即虛擬內(nèi)存管理)、內(nèi)存保護(hù)、中央處理器高速緩存的控制等等。
??在本章驅(qū)動(dòng)編寫時(shí),會(huì)使用 ioremap、iounmap 這兩個(gè)函數(shù),是用來申請(qǐng)物理地址對(duì)應(yīng)的虛擬地址的。因?yàn)樵贚inux 內(nèi)核啟動(dòng)之后,會(huì)初始化 MMU,將物理地址映射為虛擬地址。這個(gè)時(shí)候,不能向寄存器的物理基地址寫入
數(shù)據(jù),而是應(yīng)該用 ioremap 向系統(tǒng)申請(qǐng)的虛擬地址,然后在虛擬地址上進(jìn)行寄存器的寫入和讀取操作。
1.2.3MIO介紹
??ZYNQ Ultrascale+ MPSOC PS 部分的 IO 包括 PS-MIO 和 PS-EMIO,MIO 分布在 PS 的 Bank0 、BANK1 和Bank2,EMIO 分布在 PS 的 Bank3、BANK4 和 Bank5。PS-MIO 具有 78 個(gè) GPIO ,PS-EMIO 具有最多 96 個(gè)。PS-MIO的 IO 位置是固定好的,功能也是預(yù)先定義好了,而 PS-EMIO 是通過把芯片內(nèi)部 PS 的 PS-EMIO 引線接到了 PL 部分的 FPGA Pin 腳上,所以 PS-MIO 滿足必要常用的外設(shè) IO 需求,比如串口、SDIO、以太網(wǎng)等,而 PS-EMIO 按需配置,用多少,配置多少。
??芯片有 78 個(gè) MIO(multiuse I/O),它們派發(fā)在 GPIO 的,隸屬于 PS 部分。這些 IO 與 PS 直接相連。不需要添加引腳約束,MIO 信號(hào)對(duì) PL 部分是透明的,不可見。所以對(duì) MIO 的操作可以看作是純 PS 的操作。GPIO 控制和狀態(tài)寄存器是從基地址 0xFF0A_0000 開始的存儲(chǔ)器映射,并受 XPPU 保護(hù)。
PS-MIO:
Bank0:MIO[0 :25] GPIO PIN 腳號(hào):0~25
Bank1:MIO[26:51] GPIO PIN 腳號(hào):26~51
Bank2:MIO[52:77] GPIO PIN 腳號(hào):52~77
PS-EMIO:
Bank3:EMIO[ 0:31] 可以分配到任意的 FPGA IO
Bank4:EMIO[32:63] 可以分配到任意的 FPGA IO
Bank5:EMIO[64:95] 可以分配到任意的 FPGA IO
注意:GPIO [92:95]四個(gè)輸出可以用作 PL 中用戶定義邏輯的復(fù)位信號(hào)。 GPIO EMIO 信號(hào)的數(shù)量取決于在 Vivado PS
配置向?qū)В≒CW)中選擇的 PL 結(jié)構(gòu)復(fù)位的數(shù)量。 例如,如果選擇一個(gè)復(fù)位,則將 GPIO [95]分配為復(fù)位信號(hào)。如果選擇兩個(gè),則分配
GPIO [95:94]。
MIO 內(nèi)部構(gòu)造:
DATA_RO:
此寄存器使能軟件觀察 PIN 腳,當(dāng) GPIO 被配置成輸出的時(shí)候,這個(gè)寄存器的值會(huì)反應(yīng)輸出的 PIN 腳情況。
DATA:
此寄存器控制輸出到 GPIO 的值,讀這個(gè)寄存器的值可以讀到最后一次寫入該寄存器的值。
MASK_DATA_LSW:
位操作寄存器,寫入 GPIO 低 16bit 其他沒有改變的位置保存原先的狀態(tài)
MASK_DATA_MSW:
位操作寄存器,寫入 GPIO 高 16bit 其他沒有改變的位置保存原先的狀態(tài)
DIRM:
此寄存器控制輸出的開關(guān),當(dāng) DIRM[x]==0 時(shí)候,禁止輸出
OEN:
輸出使能,當(dāng) OEN[x]==0 的時(shí)候輸出關(guān)閉,PIN 腳處于三態(tài)。
因 此 , 如果要讀 IO 狀態(tài)就得讀DATA_RO 的值 , 如果是對(duì)某一位 進(jìn)行操作就是寫
MASK_DATA_LSW/MASK_DATA_MSW
1.2.4PS的LED 引腳介紹
從原理圖可以看出,當(dāng) MIO 高電平的時(shí)候,LED 可以點(diǎn)亮。低電平的時(shí)候,MIO 變暗。
還可以查到如下數(shù)據(jù):
- LED1:MIO42
- LED2:MIO40
查看 1.2.1寄存器手冊(cè),這四個(gè)管腳均屬于 bank1,如果有 MIO 在其他 bank 上,我們需要將一樣的代碼再寫一遍以控制全部LED,這里我們只控制 bank1 的 LED1 和 LED2。
1.3搭建工程
在IP核中勾選 GPIO1 MIO:
制作開發(fā)板系統(tǒng).
1.4程序分析
1.4.1驅(qū)動(dòng)程序分析
//添加頭文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
static int led_major;
static struct class *led_cls;
/* gpio 內(nèi)存映射地址 */
static unsigned long *gpio_addr = NULL;
static unsigned long *iou_slcr = NULL;
/* gpio 寄存器物理基地址 */
#define GPIO_BASE_ADDR 0xFF0A0000
#define IOU_SLCR_ADDR 0xFF180000
/* gpio 寄存器所占空間大小 */
#define GPIO_BASE_SIZE 0x368
#define IOU_SLCR_SIZE 0x714
/* gpio 引腳設(shè)置GPIO功能*/
#define GPIO_PIN_40 (unsigned int *)((unsigned long)iou_slcr + 0x000000A0)
#define GPIO_PIN_42 (unsigned int *)((unsigned long)iou_slcr + 0x000000A8)
/* gpio 控制電流0*/
#define IOU_SLCR_BANK1_CTRL0 (unsigned int *)((unsigned long)iou_slcr + 0x00000154)
/* gpio 控制電流1*/
#define IOU_SLCR_BANK1_CTRL1 (unsigned int *)((unsigned long)iou_slcr + 0x00000158)
/* gpio 輸入引腳選擇Schmitt或CMOS*/
#define IOU_SLCR_BANK1_CTRL3 (unsigned int *)((unsigned long)iou_slcr + 0x0000015C)
/* gpio 設(shè)置上下拉*/
#define IOU_SLCR_BANK1_CTRL4 (unsigned int *)((unsigned long)iou_slcr + 0x00000160)
/* gpio 使能上下拉*/
#define IOU_SLCR_BANK1_CTRL5 (unsigned int *)((unsigned long)iou_slcr + 0x00000164)
/* gpio 引腳速率選擇*/
#define IOU_SLCR_BANK1_CTRL6 (unsigned int *)((unsigned long)iou_slcr + 0x00000168)
/* gpio 方向寄存器 */
#define GPIO_DIRM_1 (unsigned int *)((unsigned long)gpio_addr + 0x00000244)
/* gpio 使能寄存器 */
#define GPIO_OEN_1 (unsigned int *)((unsigned long)gpio_addr + 0x00000248)
/* gpio 輸出數(shù)據(jù)寄存器 */
#define GPIO_DATA_1 (unsigned int *)((unsigned long)gpio_addr + 0x00000044)
/* gpio 輸出數(shù)據(jù)控制寄存器1 */
#define GPIO_MASK_DATA_1_LSW (unsigned int *)((unsigned long)gpio_addr + 0x00000008)
/* gpio 輸出數(shù)據(jù)控制寄存器2 */
#define GPIO_MASK_DATA_1_MSW (unsigned int *)((unsigned long)gpio_addr + 0x0000000C)
const struct file_operations led_fops = {};
//實(shí)現(xiàn)裝載入口函數(shù)和卸載入口函數(shù)
static __init int led_drv_v1_init(void)
{
printk("-------^v^-------\n");
printk("-led drv v1 init-\n");
//申請(qǐng)主設(shè)備號(hào)
led_major = register_chrdev(0, "led_drv_v1", &led_fops);
if (led_major < 0)
{
printk("register chrdev faile!\n");
return led_major;
}
printk("register chrdev ok!\n");
//創(chuàng)建設(shè)備節(jié)點(diǎn)
//創(chuàng)建設(shè)備的類別
led_cls = class_create(THIS_MODULE, "led_class");
printk("class create ok!\n");
//創(chuàng)建設(shè)備
device_create(led_cls, NULL, MKDEV(led_major, 0), NULL, "Myled%d", 0);
printk("device create ok!\n");
//1.內(nèi)存映射
gpio_addr = ioremap(GPIO_BASE_ADDR, GPIO_BASE_SIZE);
iou_slcr = ioremap(IOU_SLCR_ADDR, IOU_SLCR_SIZE);
printk("gpio_addr init over!\n");
//2.MIO40 MIO42 設(shè)置成GPIO,參考手冊(cè)設(shè)置
iowrite32((ioread32(GPIO_PIN_40) & 0x0), GPIO_PIN_40);
printk("gpio MIO40 init over!\n");
iowrite32((ioread32(GPIO_PIN_42) & 0x0), GPIO_PIN_42);
printk("gpio MIO42 init over!\n");
// //3.MIO40 MIO42設(shè)置輸出驅(qū)動(dòng)電流大小
// // 將 0x3FFFFFF 和 0x0 轉(zhuǎn)換為二進(jìn)制。
// // 0011 1111 1111 1111 1111 1111 1111
// // 0000 0000 0000 0000 0000 0000 0000
// // 參考手冊(cè),可以知道每一位的地址都是由 CTRL0 和 CTRL1 控制的,在這里都是 10, 查看手冊(cè)得 8 mA。
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL0) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL0);
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL1) & 0x0), IOU_SLCR_BANK1_CTRL1);
//4.選擇引腳是Schmitt還是CMOS
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL3) & 0x0), IOU_SLCR_BANK1_CTRL3);
//5.輸出管腳上下拉及使能
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL4) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL4);
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL5) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL5);
//6.MIO速率的選擇
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL6) & 0x0), IOU_SLCR_BANK1_CTRL6);
//7.MIO40 MIO42 設(shè)置成輸出
// 40 - 26 = 14, 42 - 26 = 16
// 0000 0000 000|0 0|000 0000 0000 0000
// |16 |14
// 0000 0000 000 1 0 100 0000 0000 0000
// 0000 0000 0001 0100 0000 0000 0000
// 0x00014000
iowrite32((ioread32(GPIO_DIRM_1) | 0x00014000), GPIO_DIRM_1);
//MIO40 MIO42 使能
iowrite32((ioread32(GPIO_OEN_1) | 0x00014000), GPIO_OEN_1);
/* MASK_DATA方式按滅LED1,LED2,這個(gè)需要自己修改相關(guān)參數(shù) */
//iowrite32((ioread32(GPIO_MASK_DATA_0_LSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_LSW );
//printk("GPIO_MASK_DATA_0_LSW = 0x%x\n", *GPIO_MASK_DATA_0_LSW);
//iowrite32((ioread32(GPIO_MASK_DATA_0_MSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_MSW );
//printk("GPIO_MASK_DATA_0_MSW = 0x%x\n", *GPIO_MASK_DATA_0_MSW);
//8.DATA方式按滅LED1,LED2
// 1111 1111 1111 111|1 1|111 1111 1111 1111
// |0 |0
// 1111 1111 1111 1110 1011 1111 1111 1111
// 0xFFFEBFFF
// iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEBFFF), GPIO_DATA_1);
iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEFFFF), GPIO_DATA_1);
printk("GPIO_DATA_1 = 0x%x\n", *GPIO_DATA_1);
return 0;
}
static __exit void led_drv_v1_exit(void)
{
iowrite32((ioread32(GPIO_DATA_1) | 0xFFFFFFFF), GPIO_DATA_1);
//9.內(nèi)存釋放
iounmap(gpio_addr);
iounmap(iou_slcr);
//刪除設(shè)備
device_destroy(led_cls, MKDEV(led_major, 0));
//刪除類
class_destroy(led_cls);
//注銷主設(shè)備號(hào)
unregister_chrdev(led_major, "led_drv_v1");
printk("-------^v^-------\n");
printk("-led drv v1 exit-\n");
}
//申明裝載入口函數(shù)和卸載入口函數(shù)
module_init(led_drv_v1_init);
module_exit(led_drv_v1_exit);
//添加GPL協(xié)議
MODULE_LICENSE("GPL");
MODULE_AUTHOR("msxbo");
行 9~11,用來儲(chǔ)存映射后虛擬地址的基地址。87、88 行賦值。
行 13~15,是我們要控制的兩個(gè)寄存器物理地址的基地址。
行 16~18,是兩個(gè)寄存器的完整大小,在地址轉(zhuǎn)換的時(shí)候直接映射整個(gè)寄存器,方便理解。
行 20~22,定義設(shè)置復(fù)用功能的寄存器。
行 24~35,六個(gè)控制電氣特性的寄存器。
行 37~46,GPIO 的控制寄存器。
行 113~117,是使用 mask_data 的方法熄滅 led,和 data 方法熄滅效果一樣。
行 119~126,使用 data 方法熄滅 led。
1:內(nèi)存映射
static __init int led_drv_v1_init(void)
{
//1.內(nèi)存映射
gpio_addr = ioremap(GPIO_BASE_ADDR, GPIO_BASE_SIZE);
iou_slcr = ioremap(IOU_SLCR_ADDR, IOU_SLCR_SIZE);
printk("gpio_addr init over!\n");
return 0;
}
含義:內(nèi)存映射的目的是為了把物理地址映射為虛擬地址,方便對(duì)寄存器進(jìn)行操作。
-
GPIO_BASE_ADDR:這是寄存器手冊(cè)中查詢到的“GPIO Module”的“Base Address”。
- GPIO_BASE_SIZE:這是該寄存器的容量,也就是“GPIO Module”的容量。從“GPIO Module”的最后一行可以看到,它的最后一位是“0x0000000364”,位寬是 32 位。簡單的算下,就是 4 字節(jié),所以“GPIO Module”的容量為“0x0000000368”。
2:設(shè)置 MIO 功能
static __init int led_drv_v1_init(void)
{
//2.MIO40 MIO42 設(shè)置成GPIO,參考手冊(cè)設(shè)置
iowrite32((ioread32(GPIO_PIN_40) & 0x0), GPIO_PIN_40);
printk("gpio MIO40 init over!\n");
iowrite32((ioread32(GPIO_PIN_42) & 0x0), GPIO_PIN_42);
printk("gpio MIO42 init over!\n");
return 0;
}
含義:將 MIO 的功能設(shè)置為 GPIO。
具體分析:
? iowrite32:這個(gè)函數(shù)會(huì)把前面的值寫入到后面的目的地址。
? ioread32:這個(gè)函數(shù)則是讀取目的地址的值。
? 0x0:這個(gè)部分需要查詢寄存器手冊(cè)的“IOU_SLCR Module”部分,找到需要設(shè)置的 MIO 引腳的序號(hào),然后根據(jù)自己的需要設(shè)置手冊(cè)中的相應(yīng)的值。
?GPIO_PIN_40:這是在之前確定好的“iou_slcr”進(jìn)行偏移的地址。偏移地址可以在寄存器手冊(cè)中查詢到
3:設(shè)置 MIO 輸出電流的大小
static __init int led_drv_v1_init(void)
{
// //3.MIO40 MIO42設(shè)置輸出驅(qū)動(dòng)電流大小
// // 將 0x3FFFFFF 和 0x0 轉(zhuǎn)換為二進(jìn)制。
// // 0011 1111 1111 1111 1111 1111 1111
// // 0000 0000 0000 0000 0000 0000 0000
// // 參考手冊(cè),可以知道每一位的地址都是由 CTRL0 和 CTRL1 控制的,在這里都是 10, 查看手冊(cè)得 8 mA。
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL0) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL0);
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL1) & 0x0), IOU_SLCR_BANK1_CTRL1);
return 0;
}
含義:將 Bank1 的電流都設(shè)置為 8mA,Bank1 控制的是 MIO 引腳 [26:51]。
具體分析:
寄存器部分的操作都是按位計(jì)算的,想要什么功能,在手冊(cè)中查到之后,就需要通過寄存器把想要控制的引腳的地址寫入對(duì)應(yīng)功能所需的設(shè)置。這里以控制電流為例。因?yàn)樾枰刂频囊_為 MIO40 和 MIO42,40 和 42 都是 bank1 所負(fù)責(zé)的區(qū)域。因此,在寄存器手冊(cè)中找到對(duì)應(yīng)的bank1 部分。
??這個(gè)部分的寄存器都是控制 bank1 部分功能的,我們先介紹 ctrl0 和 ctrl1 兩個(gè)部分。這兩個(gè)部分是控制電流的。
??通過查看“bank1_crtl0 (IOU_SLCR) Register”的“Description”可以知道,ctrl0 得和 ctrl1 兩個(gè)部分才能控制電流的大小。
我們需要控制的是 MIO40 和 MIO42,這里為了方便起見,將 ctrl0 全部設(shè)置為 1,將 ctrl1 全部設(shè)置為 0,查表可以知道是 8mA。
- &:按位與,可以理解為交集,交集是指 A 與 B 共有的。
- | :按位或,可以理解為并集,并集是指 A 和 B 所有的。
4:設(shè)置引腳是 Schmitt Trigger 還是 CMOS Input
static __init int led_drv_v1_init(void)
{
//4.選擇引腳是Schmitt還是CMOS
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL3) & 0x0), IOU_SLCR_BANK1_CTRL3);
return 0;
}
含義:設(shè)置 MIO 引腳的功能,這里選擇 CMOS Input。
5:設(shè)置引腳的上下拉及使能
static __init int led_drv_v1_init(void)
{
//5.輸出管腳上下拉及使能
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL4) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL4);
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL5) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL5);
return 0;
}
含義:設(shè)置 MIO 引腳的上拉或者下拉,并且使能。
具體分析:在 ctrl4 部分將其全部設(shè)置為 1,對(duì)照寄存器手冊(cè)可以知道是上拉,并且 bank1 上所有的都是上拉。同理在 ctrl5部分也將其全部設(shè)置為 1,對(duì)照寄存器手冊(cè)可以看到 bank1 上 [26:51] 的 MIO 都被啟動(dòng)了。
6:設(shè)置 MIO 的速率
static __init int led_drv_v1_init(void)
{
//6.MIO速率的選擇
iowrite32((ioread32(IOU_SLCR_BANK1_CTRL6) & 0x0), IOU_SLCR_BANK1_CTRL6);
return 0;
}
Ctrl6 可以設(shè)置 MIO 的速率,0 為低速,1 為高速。
7:設(shè)置 GPIO 的輸入輸出
static __init int led_drv_v1_init(void)
{
//7.MIO40 MIO42 設(shè)置成輸出
// 40 - 26 = 14, 42 - 26 = 16
// 0000 0000 000|0 0|000 0000 0000 0000
// |16 |14
// 0000 0000 000 1 0 100 0000 0000 0000
// 0000 0000 0001 0100 0000 0000 0000
// 0x00014000
iowrite32((ioread32(GPIO_DIRM_1) | 0x00014000), GPIO_DIRM_1);
//MIO40 MIO42 使能
iowrite32((ioread32(GPIO_OEN_1) | 0x00014000), GPIO_OEN_1);
return 0;
}
含義:將 GPIO 的輸入輸出部分,MIO40 和 MIO42 的地方設(shè)置為輸出。
具體分析:
這個(gè)部分設(shè)置的是 GPIO 的相關(guān)功能,所以要去“GPIO Module”部分去找相關(guān)的寄存器的功能。
-
GPIO_DIRM_1:是指控制 GPIO 的輸入輸出方向。
-
GPIO_OEN_1:則是用來控制使能,也就是 enable 的意思。
8:通過 Data 的方式熄滅 LED
static __init int led_drv_v1_init(void)
{
/* MASK_DATA方式按滅LED1,LED2,這個(gè)需要自己修改相關(guān)參數(shù) */
//iowrite32((ioread32(GPIO_MASK_DATA_0_LSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_LSW );
//printk("GPIO_MASK_DATA_0_LSW = 0x%x\n", *GPIO_MASK_DATA_0_LSW);
//iowrite32((ioread32(GPIO_MASK_DATA_0_MSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_MSW );
//printk("GPIO_MASK_DATA_0_MSW = 0x%x\n", *GPIO_MASK_DATA_0_MSW);
//8.DATA方式按滅LED1,LED2
// 1111 1111 1111 111|1 1|111 1111 1111 1111
// |0 |0
// 1111 1111 1111 1110 1011 1111 1111 1111
// 0xFFFEBFFF
// iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEBFFF), GPIO_DATA_1);
iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEFFFF), GPIO_DATA_1);
printk("GPIO_DATA_1 = 0x%x\n", *GPIO_DATA_1);
return 0;
}
含義:將已經(jīng)配置 GPIO 的 MIO40,MIO42 的輸出設(shè)置為 0,也就是關(guān)閉輸出。
具體分析:
這是通過控制 DATA 寄存器的方式熄滅 LED,還可以使用 MASK_DATA 的方式熄滅 LED。具體的設(shè)置的值需要自己查寄存器手冊(cè)修改。DATA 寄存器的說明在介紹部分。文章來源:http://www.zghlxwxcb.cn/news/detail-622203.html
9:內(nèi)存釋放
static __init int led_drv_v1_init(void)
{
//9.內(nèi)存釋放
iounmap(gpio_addr);
iounmap(iou_slcr);
return 0;
}
含義:將申請(qǐng)的虛擬內(nèi)存資源進(jìn)行釋放。
具體分析:
在通過 ioremap 向系統(tǒng)申請(qǐng)了系統(tǒng)資源之后,在驅(qū)動(dòng)退出的時(shí)候,需要將資源釋放。釋放用 iounmap 函數(shù)。文章來源地址http://www.zghlxwxcb.cn/news/detail-622203.html
1.5交叉編譯
- 將led_drv_v1.c和Makefile文件在虛擬機(jī)中進(jìn)行編譯。
- 將編譯生成的led_drv_v1.ko文件上傳到開發(fā)板的Linux中。
1.6演示
- 在終端中使用輸入“l(fā)s”,查看是否存在“l(fā)ed_drv_v1.ko”文件。
- 確認(rèn)存在之后,進(jìn)入 root 權(quán)限,然后在終端輸入“insmod led_drv_v1.ko”。
輸入
- “l(fā)smod”查看是否安裝成功。
- 在終端輸入“rmmod led_drv_v1.ko”,PS的燈會(huì)亮起。
到了這里,關(guān)于【ZYNQ】Linux驅(qū)動(dòng)之夢(mèng)開始的地方的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!