使用一個外設之前,你要弄明白這個外設是干什么的,它是怎樣作業的,它的輸入輸出介面都是啥,你還要知道怎樣給外設分配地址,
GPIO的具體硬體結構在這里不多說了,(如果你要自己寫代碼,那硬體結構必須弄得明明白白,)我們用到的GPIO模塊是ARM公司提供的,拿來用就行,GPIO的硬體結構懂個大概就行,
寫外設的時候,往往要和暫存器打交道,暫存器分為片內暫存器和外部介面暫存器,內部暫存器就是R0-R15,R0-R15里面有通用暫存器,有特殊功能暫存器,這個大家應該都清楚,下面括號內容可以不看,
(暫存器就是存盤單元,給有特定功能的記憶體單元取一個別名,這個別名就是我們經常說的暫存器,比如PC指標暫存器,這個給已經分配好地址的有特定功能的記憶體單元取別名的程序就叫暫存器映射,訪問暫存器要使用結構體指標,
那GPIO有哪些暫存器呢,這些暫存器是干什么的??
暫存器用于 GPIO 的控制,使用 4 個暫存器即可操作一個 GPIO,可以很方便的實作 GPIO 的各種應用,這4個暫存器分別是
1.控制方向的暫存器
2.控制輸出電平狀態的2個暫存器
3.反映引腳電平狀態的暫存器
通過地址操控這些暫存器,就可以使用GPIO,硬體代碼里要給GPIO這個從設備分配地址,KEIL軟體代碼里要寫好結構體,通過結構體控制GPIO,打個比方,硬體代碼就好比水箱,寫軟體代碼好比往水箱里灌水,
例程以2個16位GPIO為例,需要幾個埠就定義幾個,
在system.v這個系統檔案中定義GPIO地址(定義地址的目的是讓CPU訪問它,后面還要對地址解碼),定義埠,定義gpio的中斷,系統檔案就是和總體架構檔案,有大量的例化,注意,系統檔案我們要自己修改,
parameter BASEADDR_GPIO0 = 32'h4001_0000, // GPIO0 peripheral base address
parameter BASEADDR_GPIO1 = 32'h4001_1000, // GPIO1 peripheral base address
input wire [15:0] p0_in, // GPIO 0 inputs
output wire [15:0] p0_out, // GPIO 0 outputs
output wire [15:0] p0_outen, // GPIO 0 output enables
output wire [15:0] p0_altfunc, // GPIO 0 alternate function (pin mux)
input wire [15:0] p1_in, // GPIO 1 inputs
output wire [15:0] p1_out, // GPIO 1 outputs
output wire [15:0] p1_outen, // GPIO 1 output enables
output wire [15:0] p1_altfunc, // GPIO 1 alternate function (pin mux)
wire gpio0_hsel; // AHB GPIO bus interface signals
wire gpio0_hreadyout;
wire [31:0] gpio0_hrdata;
wire gpio0_hresp;
wire gpio1_hsel; // AHB GPIO bus interface signals
wire gpio1_hreadyout;
wire [31:0] gpio1_hrdata;
wire gpio1_hresp;
wire [15:0] gpio0_intr;
wire gpio0_combintr;
wire gpio1_combintr;
還要把以上定義的系統引數例化到地址解碼檔案(decode.v)中,地址解碼就相當于多路選擇器,根據地址多選1,選中你需要的外設模塊進行訪問,在系統檔案最重要的就是把GPIO掛到系統總線上,解碼和掛總線如下,其中掛總線部分省略了大量無關代碼:
// AHB address decode
cmsdk_mcu_addr_decode #(
.BASEADDR_GPIO0 (BASEADDR_GPIO0),
.BASEADDR_GPIO1 (BASEADDR_GPIO1),
.BOOT_LOADER_PRESENT (BOOT_LOADER_PRESENT)
)
u_addr_decode (
// System Address
.code_haddr (code_haddr),
.code_hsel (code_hsel),
.sys_haddr (sys_haddr),
.sys_hsel (sys_hsel),
.remap_ctrl (remap_ctrl),
.boot_hsel (boot_hsel),
.flash_hsel (flash_hsel),
.sram_hsel (sram_hsel),
.apbsys_hsel (apbsys_hsel),
.gpio0_hsel (gpio0_hsel),
.gpio1_hsel (gpio1_hsel),
.sysctrl_hsel (sysctrl_hsel),
.defslv0_hsel (defslv0_hsel),
.defslv1_hsel (defslv1_hsel)
);
//把GPIO掛到總線上
.HSEL5 (gpio0_hsel), // Input Port 5
.HREADYOUT5 (gpio0_hreadyout),
.HRESP5 (gpio0_hresp),
.HRDATA5 (gpio0_hrdata),
.HSEL6 (gpio1_hsel), // Input Port 6
.HREADYOUT6 (gpio1_hreadyout),
.HRESP6 (gpio1_hresp),
.HRDATA6 (gpio1_hrdata),
當然,別忘了把gpio模塊例化進來,如下:
cmsdk_ahb_gpio #(
.ALTERNATE_FUNC_MASK (16'h0000), // No pin muxing for Port #0
.ALTERNATE_FUNC_DEFAULT (16'h0000), // All pins default to GPIO
.BE (BE)
)
u_ahb_gpio_0 (
// AHB Inputs
.HCLK (HCLKSYS),
.HRESETn (HRESETn),
.FCLK (FCLK),
.HSEL (gpio0_hsel),
.HREADY (sys_hready),
.HTRANS (sys_htrans),
.HSIZE (sys_hsize),
.HWRITE (sys_hwrite),
.HADDR (sys_haddr[11:0]),
.HWDATA (sys_hwdata),
// AHB Outputs
.HREADYOUT (gpio0_hreadyout),
.HRESP (gpio0_hresp),
.HRDATA (gpio0_hrdata),
.ECOREVNUM (4'h0),// Engineering-change-order revision bits
.PORTIN (p0_in), // GPIO Interface inputs
.PORTOUT (p0_out), // GPIO Interface outputs
.PORTEN (p0_outen),
.PORTFUNC (p0_altfunc), // Alternate function control
.GPIOINT (gpio0_intr[15:0]), // Interrupt outputs
.COMBINT (gpio0_combintr)
);
cmsdk_ahb_gpio #(
.ALTERNATE_FUNC_MASK (16'hF82A), // pin muxing for Port #1
.ALTERNATE_FUNC_DEFAULT (16'h0000), // All pins default to GPIO
.BE (BE)
)
u_ahb_gpio_1 (
// AHB Inputs
.HCLK (HCLKSYS),
.HRESETn (HRESETn),
.FCLK (FCLK),
.HSEL (gpio1_hsel),
.HREADY (sys_hready),
.HTRANS (sys_htrans),
.HSIZE (sys_hsize),
.HWRITE (sys_hwrite),
.HADDR (sys_haddr[11:0]),
.HWDATA (sys_hwdata),
// AHB Outputs
.HREADYOUT (gpio1_hreadyout),
.HRESP (gpio1_hresp),
.HRDATA (gpio1_hrdata),
.ECOREVNUM (4'h0),// Engineering-change-order revision bits
.PORTIN (p1_in), // GPIO Interface inputs
.PORTOUT (p1_out), // GPIO Interface outputs
.PORTEN (p1_outen),
.PORTFUNC (p1_altfunc), // Alternate function control
.GPIOINT (), // Interrupt outputs
.COMBINT (gpio1_combintr)
);
cmsdk_ahb_gpio.v這個檔案是不需要你寫的,添加進來即可,這個檔案里還包括2個和gpio相關的檔案,這2個檔案是用來實作gpio底層邏輯的,都是ARM提供,添加進來即可,
再來講一下decode.v檔案,這個也是需要我們自己修改的,每添加一個外設,就要相應修改decode檔案中的代碼,下面是對外設解碼的相關邏輯,
// ----------------------------------------------------------
// Peripheral Selection decode logic
// ----------------------------------------------------------
assign apbsys_hsel = (sys_haddr[31:16]==16'h4000) &
sys_hsel; // 0x40000000
assign gpio0_hsel = (sys_haddr[31:12]==
BASEADDR_GPIO0[31:12]) & sys_hsel; // 0x40010000
assign gpio1_hsel = (sys_haddr[31:12]==
BASEADDR_GPIO1[31:12]) & sys_hsel; // 0x40011000
assign sysctrl_hsel = (sys_haddr[31:12]==20'h4001F) &
sys_hsel;
在頂層module中,把IO設定為inout資料型別,在頂層模塊定義一個引數parameter,用來設定GPIO模塊的基地址,把引數都例化到system.v這個檔案
inout wire [15:0] P0,
inout wire [15:0] P1,
localparam BASEADDR_GPIO0 = 32'h4001_0000; //外設基地址
localparam BASEADDR_GPIO1 = 32'h4001_1000;
頂層定義幾個連線變數,用于例化到system.v中
wire [15:0] p1_in; // I/O port #1 input
wire [15:0] p1_out; // I/O port #1 output
wire [15:0] p1_outen; // I/O port #1 output enable (tristate buffer control)
wire [15:0] p1_altfunc; // I/O port #1 alternate function
例化到system.v
// IO Ports
.p0_in (p0_in),
.p0_out (p0_out),
.p0_outen (p0_outen),
.p0_altfunc (p0_altfunc),
.p1_in (p1_in),
.p1_out (p1_out),
.p1_outen (p1_outen),
.p1_altfunc (p1_altfunc),
除了埠要例化,GPIO外設基地址也要例化,
.BASEADDR_GPIO0 (BASEADDR_GPIO0), // GPIO0 Base Address
.BASEADDR_GPIO1 (BASEADDR_GPIO1), // GPIO1 Base Address
外設基地址既不是輸入變數,也不是輸出變數,例化時要把它寫道"#()"的括號里,學過verilog的人應該都知道,
至此,gpio已經刮到了總線上,接下來就是寫用keil軟體的程序,我們需要在啟動檔案添加一些中斷,還要寫一些結構體,用于設定GPIO的各種引數,給GPIO結構體中變數賦值的程序就是寫暫存器的程序,我們透過結構體操控了暫存器,進而控制了GPIO,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286468.html
標籤:其他
上一篇:嵌入式考點
下一篇:RTOS概述
