python下载第三方库no module namedpio_實驗:PIO外部中斷

實驗:PIO外部中斷

Author:Chen Tuo

Copyright © Chen Tuo, 2008-2009. All rights reserved.

2008年3月1日創建

2009年3月26日修改

1.實驗目的

熟悉NiosII PIO設備的訪問方法。學習邊沿觸發中斷。

2.實驗設備

硬件:PC機,EP1C3或EP1C12核心板,MyEpx3實驗版。

軟件:Quartus II 7.2;MagaCore 7.2;Nios II IDE 7.2。

3.實驗內容

用一個按鍵控制一個LED燈顯示。

4.實驗預習要求

熟悉PIO外設的訪問和控制和PIO的中斷控制機制。

5.實驗步驟

(1)創建一個Quartus II項目PIOEINT。

(2)如下圖,定制一個Nios II CPU,命名為myniosii。

7f30ce9732e664e5dc0c018f7f3961a9.png

其中:

lonchip_mem為8K。啟動向量和異常向量配置如下:

e485d01e10d4ab413b21270e73ed4b57.png

lKEY_PIO為1位輸入,如下圖設置為下降沿觸發中斷。

3d4308b1372c877f9a8cb98731957b85.png

lLED_PIO為1位輸出。

(3)在Quartus II中創建一個原理圖,放置一個上面定制好的Nios II核myniosii,並如圖設置引腳。編譯、下載硬件配置到FPGA。

87c0e983e95b2a1c3a7c7ec66fff5157.png

(4)啟動Nios II IDE,新建一個空白Nios II C/C++ Application項目,並命名為pio_eint。

(5)在pio_eint項目中添加一個C文件pio_eint.c。

(6)設置pio_eint項目Properties屬性,對該項目的代碼編譯進行優化:

C/C++ Build > Tool Settings > General > Optimize size (-Os)

(7)設置pio_eint_syslib項目Properties屬性,對該項目的代碼編譯進行優化:

a)C/C++ Build > Tool Settings > General > Optimize size (-Os)

b)在System library屬性頁面,清除Support C++和Clean exit (flush buffers);選上Program、LightWeight device driver API、Reduced device drivers和Small C library。

(8)編譯、調試、運行。

c93a82a02389ba1b08f0d4262c58abbf.png

從上面的編譯、鏈接信息看到,優化后,pio_eint.elf文件代碼加指令總共只有1728字節,這樣實驗就完全可以在低成本的EP1C3系列芯片上進行。

6.程序清單

PIO外部中斷實驗程序

/*************************************************************

*文件名:pio_eint.c

*功能:用按鍵以中斷方式控制LED。每當有一次按鍵中斷時,取反LED一次

*說明:按下KEY1觀察LED1的狀態

************************************************************/

#include

#include "system.h"

#include "altera_avalon_pio_regs.h"

#include "alt_types.h"

#include "sys/alt_irq.h"

#include "priv/alt_busy_sleep.h"

#define LEDCON 0x01

#define KEYCON 0x01

volatile alt_u32 done = 0;     //信號量:通知中斷事件發生

/*************************************************************

*名稱:KeyDown_interrupts()

*功能:鍵按下事件中斷服務子程序,當鍵按下時,通過down標志告知外界

*入口參數:context,一般用於傳遞中斷狀態寄存器的值,這里未使用

*         id,中斷號,這里未使用

*出口參數:無

************************************************************/

static void KeyDown_interrupts(void* context, alt_u32 id)

{

/*清中斷捕獲寄存器*/

IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_KEY_BASE, ~KEYCON);

/*通知外部有中斷事件發生*/

done++;

}

/*************************************************************

*名稱:InitPIO()

*功能:初始化PIO_KEY為輸入,PIO_LED為輸出,開中斷,清邊沿捕獲寄存器

*入口參數:無

*出口參數:無

************************************************************/

void InitPIO(void)

{

/*初始化PIO_KEY為輸入,PIO_LED為輸出*/

IOWR_ALTERA_AVALON_PIO_DIRECTION(PIO_KEY_BASE, ~KEYCON); //0為輸入

IOWR_ALTERA_AVALON_PIO_DIRECTION(PIO_LED_BASE, LEDCON); //1為輸出

/*開PIO_KEY中斷*/

IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_KEY_BASE, KEYCON);

/*清邊沿捕獲寄存器*/

IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_KEY_BASE, ~KEYCON);

/*注冊中斷服務子程序*/

alt_irq_register(PIO_KEY_IRQ, NULL, KeyDown_interrupts);

}

/*************************************************************

*名稱:main()

*功能:等待按鍵中斷,並輸出控制LED

************************************************************/

int main(void)

{

volatile alt_u32 key_state, old_state, new_state;

old_state = KEYCON;

IOWR_ALTERA_AVALON_PIO_DATA(PIO_LED_BASE, old_state); //初始化LED熄滅

InitPIO();

while(1)

{

if(0 != done)

{

done--;                          //中斷事件數量減1

alt_busy_sleep(5000);          //延時5ms

key_state = IORD_ALTERA_AVALON_PIO_DATA(PIO_KEY_BASE)&KEYCON;

if(key_state == 0xFF)          //如果是由短暫脈沖引起的中斷,則忽略

continue;                   //消除鍵盤抖動

new_state = ~(old_state^key_state); //按鍵按下時LED取反。

old_state = new_state;         //保存LED的狀態

IOWR_ALTERA_AVALON_PIO_DATA(PIO_LED_BASE, new_state);

}

}

return(0);

}

注意:不要在中斷服務程序中進行等待或者其他阻塞性操作。

7.中斷服務程序的調試

l在中斷服務程序內設置斷點。當中斷發生后,處理器會在斷點處停下,用戶可以單步調試中斷服務程序。

l使用spintf()函數把關鍵數據寫到內存中,然后觸發外部分析程序。在中斷服務程序中,不可調用printf()函數,因為它可能引起阻塞且運行時間無法預知,但可以調用sprintf()。

8.鍵盤去抖動

由於按鍵的按下與抬起都會有10~20ms的抖動毛刺存在,如下圖所示。因此,為了獲取穩定的按鍵信息,須要避開這段抖動期。

3078e1bf4331a07fe1bb52819d11a94d.png

去抖動的方法有很多種,如使用R-S觸發器的硬件方法、運用不同算法的各種軟件方法等。硬件方法會增加成本和體積,對於按鍵較多的矩陣式鍵盤,一般會使用硬件方法;一般情況下軟件方法用的比較普遍,但加固定延時的去抖動法效率最低,它以無謂地耗費機時來實現去抖動。本例采用短延時加異或算法消除鍵盤抖動。

9.system.h

system.h文件在路徑"PIOEINT"software"pio_eint_syslib"Debug"system_description下。注意:system.h要編譯以后才有。system.h頭文件是根據SOPC Builder生成的myniosii.ptf文件產生的系統硬件信息的宏定義文件,清單如下:

/* system.h

*

* Machine generated for a CPU named "cpu" as defined in:

* d:"lecture"embed"FPGA"SOPCExample1C12"PIO"PIOEINT"software"pio_eint_syslib"..".."myniosii.ptf

*

* Generated: 2009-03-31 07:55:41.873

*

*/

#ifndef __SYSTEM_H_

#define __SYSTEM_H_

/*

* system configuration

*

*/

#define ALT_SYSTEM_NAME "myniosii"

#define ALT_CPU_NAME "cpu"

#define ALT_CPU_ARCHITECTURE "altera_nios2"

#define ALT_DEVICE_FAMILY "CYCLONE"

#define ALT_STDIN "/dev/null"

#define ALT_STDIN_TYPE ""

#define ALT_STDIN_BASE UNDEFINED VARIABLE %BASE_ADDRESS%

#define ALT_STDIN_DEV null

#define ALT_STDOUT "/dev/null"

#define ALT_STDOUT_TYPE ""

#define ALT_STDOUT_BASE UNDEFINED VARIABLE %BASE_ADDRESS%

#define ALT_STDOUT_DEV null

#define ALT_STDERR "/dev/null"

#define ALT_STDERR_TYPE ""

#define ALT_STDERR_BASE UNDEFINED VARIABLE %BASE_ADDRESS%

#define ALT_STDERR_DEV null

#define ALT_CPU_FREQ 50000000

#define ALT_IRQ_BASE NULL

/*

* processor configuration

*

*/

#define NIOS2_CPU_IMPLEMENTATION "tiny"

#define NIOS2_BIG_ENDIAN 0

#define NIOS2_ICACHE_SIZE 0

#define NIOS2_DCACHE_SIZE 0

#define NIOS2_ICACHE_LINE_SIZE 0

#define NIOS2_ICACHE_LINE_SIZE_LOG2 0

#define NIOS2_DCACHE_LINE_SIZE 0

#define NIOS2_DCACHE_LINE_SIZE_LOG2 0

#define NIOS2_FLUSHDA_SUPPORTED

#define NIOS2_EXCEPTION_ADDR 0x00002020

#define NIOS2_RESET_ADDR 0x00002000

#define NIOS2_BREAK_ADDR 0x00004820

#define NIOS2_HAS_DEBUG_STUB

#define NIOS2_CPU_ID_SIZE 1

#define NIOS2_CPU_ID_VALUE 0

/*

* A define for each class of peripheral

*

*/

#define __ALTERA_AVALON_ONCHIP_MEMORY2

#define __ALTERA_AVALON_PIO

/*

* onchip_mem configuration

*

*/

#define ONCHIP_MEM_NAME "/dev/onchip_mem"

#define ONCHIP_MEM_TYPE "altera_avalon_onchip_memory2"

#define ONCHIP_MEM_BASE 0x00002000

#define ONCHIP_MEM_SPAN 8192

#define ONCHIP_MEM_ALLOW_MRAM_SIM_CONTENTS_ONLY_FILE 0

#define ONCHIP_MEM_RAM_BLOCK_TYPE "M4K"

#define ONCHIP_MEM_INIT_CONTENTS_FILE "onchip_mem"

#define ONCHIP_MEM_NON_DEFAULT_INIT_FILE_ENABLED 0

#define ONCHIP_MEM_GUI_RAM_BLOCK_TYPE "Automatic"

#define ONCHIP_MEM_WRITEABLE 1

#define ONCHIP_MEM_DUAL_PORT 0

#define ONCHIP_MEM_SIZE_VALUE 8192

#define ONCHIP_MEM_SIZE_MULTIPLE 1

#define ONCHIP_MEM_USE_SHALLOW_MEM_BLOCKS 0

#define ONCHIP_MEM_INIT_MEM_CONTENT 1

#define ONCHIP_MEM_ALLOW_IN_SYSTEM_MEMORY_CONTENT_EDITOR 0

#define ONCHIP_MEM_INSTANCE_ID "NONE"

#define ONCHIP_MEM_IGNORE_AUTO_BLOCK_TYPE_ASSIGNMENT 1

#define ONCHIP_MEM_CONTENTS_INFO "QUARTUS_PROJECT_DIR/onchip_mem.hex 1238111903"

#define ALT_MODULE_CLASS_onchip_mem altera_avalon_onchip_memory2

/*

* pio_key configuration

*

*/

#define PIO_KEY_NAME "/dev/pio_key"

#define PIO_KEY_TYPE "altera_avalon_pio"

#define PIO_KEY_BASE 0x00005000

#define PIO_KEY_SPAN 16

#define PIO_KEY_IRQ 0

#define PIO_KEY_DO_TEST_BENCH_WIRING 0

#define PIO_KEY_DRIVEN_SIM_VALUE 0

#define PIO_KEY_HAS_TRI 0

#define PIO_KEY_HAS_OUT 0

#define PIO_KEY_HAS_IN 1

#define PIO_KEY_CAPTURE 1

#define PIO_KEY_DATA_WIDTH 1

#define PIO_KEY_EDGE_TYPE "RISING"

#define PIO_KEY_IRQ_TYPE "EDGE"

#define PIO_KEY_BIT_CLEARING_EDGE_REGISTER 0

#define PIO_KEY_FREQ 50000000

#define ALT_MODULE_CLASS_pio_key altera_avalon_pio

/*

* pio_led configuration

*

*/

#define PIO_LED_NAME "/dev/pio_led"

#define PIO_LED_TYPE "altera_avalon_pio"

#define PIO_LED_BASE 0x00005010

#define PIO_LED_SPAN 16

#define PIO_LED_DO_TEST_BENCH_WIRING 0

#define PIO_LED_DRIVEN_SIM_VALUE 0

#define PIO_LED_HAS_TRI 0

#define PIO_LED_HAS_OUT 1

#define PIO_LED_HAS_IN 0

#define PIO_LED_CAPTURE 0

#define PIO_LED_DATA_WIDTH 1

#define PIO_LED_EDGE_TYPE "NONE"

#define PIO_LED_IRQ_TYPE "NONE"

#define PIO_LED_BIT_CLEARING_EDGE_REGISTER 0

#define PIO_LED_FREQ 50000000

#define ALT_MODULE_CLASS_pio_led altera_avalon_pio

/*

* system library configuration

*

*/

#define ALT_MAX_FD 32

#define ALT_SYS_CLK none

#define ALT_TIMESTAMP_CLK none

/*

* Devices associated with code sections.

*

*/

#define ALT_TEXT_DEVICE       ONCHIP_MEM

#define ALT_RODATA_DEVICE     ONCHIP_MEM

#define ALT_RWDATA_DEVICE     ONCHIP_MEM

#define ALT_EXCEPTIONS_DEVICE ONCHIP_MEM

#define ALT_RESET_DEVICE      ONCHIP_MEM

/*

* The text section is initialised so no bootloader will be required.

* Set a variable to tell crt0.S to provide code at the reset address and

* to initialise rwdata if appropriate.

*/

#define ALT_NO_BOOTLOADER

#endif /* __SYSTEM_H_ */

10.altera_avalon_pio_regs.h

altera_avalon_pio_regs.h位於路徑C:"altera"72"ip"sopc_builder_ip"altera_avalon_pio"inc下。該頭文件提供PIO內核寄存器訪問宏定義,程序中對I/O端口操作的宏定義都在該文件中。其清單如下:

#ifndef __ALTERA_AVALON_PIO_REGS_H__

#define __ALTERA_AVALON_PIO_REGS_H__

#include

#define IOADDR_ALTERA_AVALON_PIO_DATA(base)           __IO_CALC_ADDRESS_NATIVE(base, 0)

#define IORD_ALTERA_AVALON_PIO_DATA(base)             IORD(base, 0)

#define IOWR_ALTERA_AVALON_PIO_DATA(base, data)       IOWR(base, 0, data)

#define IOADDR_ALTERA_AVALON_PIO_DIRECTION(base)      __IO_CALC_ADDRESS_NATIVE(base, 1)

#define IORD_ALTERA_AVALON_PIO_DIRECTION(base)        IORD(base, 1)

#define IOWR_ALTERA_AVALON_PIO_DIRECTION(base, data) IOWR(base, 1, data)

#define IOADDR_ALTERA_AVALON_PIO_IRQ_MASK(base)       __IO_CALC_ADDRESS_NATIVE(base, 2)

#define IORD_ALTERA_AVALON_PIO_IRQ_MASK(base)         IORD(base, 2)

#define IOWR_ALTERA_AVALON_PIO_IRQ_MASK(base, data)   IOWR(base, 2, data)

#define IOADDR_ALTERA_AVALON_PIO_EDGE_CAP(base)       __IO_CALC_ADDRESS_NATIVE(base, 3)

#define IORD_ALTERA_AVALON_PIO_EDGE_CAP(base)         IORD(base, 3)

#define IOWR_ALTERA_AVALON_PIO_EDGE_CAP(base, data)   IOWR(base, 3, data)

/* Defintions for direction-register operation with bi-directional PIOs */

#define ALTERA_AVALON_PIO_DIRECTION_INPUT 0

#define ALTERA_AVALON_PIO_DIRECTION_OUTPUT 1

#endif /* __ALTERA_AVALON_PIO_REGS_H__ */

下面是與中斷屏蔽寄存器相關的宏定義說明。

中斷屏蔽寄存器訪問宏定義

宏定義

意義

IOADDR_ALTERA_AVALON_PIO_IRQ_MASK(base)

計算中斷屏蔽寄存器的物理地址。參數base為PIO內核的基地址

IORD_ALTERA_AVALON_PIO_IRQ_MASK(base)

讀取中斷屏蔽寄存器的內容

IOWR_ALTERA_AVALON_PIO_IRQ_MASK(base, data)

寫中斷屏蔽寄存器。參數data為向中斷屏蔽寄存器寫入的數據

11.alt_types.h

alt_types.h位於路徑C:"altera"72"nios2eds"components"altera_nios2"HAL"inc下。它定義與altera Nios II自己的與編譯器無關的數據類型。其清單如下:

#ifndef __ALT_TYPES_H__

#define __ALT_TYPES_H__

/*

* Don't declare these typedefs if this file is included by assembly source.

*/

#ifndef ALT_ASM_SRC

typedef signed char alt_8;

typedef unsigned char alt_u8;

typedef signed short alt_16;

typedef unsigned short alt_u16;

typedef signed long alt_32;

typedef unsigned long alt_u32;

typedef long long alt_64;

typedef unsigned long long alt_u64;

#endif

#define ALT_INLINE        __inline__

#define ALT_ALWAYS_INLINE __attribute__ ((always_inline))

#define ALT_WEAK          __attribute__((weak))

#endif /* __ALT_TYPES_H__ */

12.sys/alt_irq.h

sys/alt_irq.h位於路徑C:"altera"72"nios2eds"components"altera_nios2"HAL"inc"sys下。它定義了與中斷相關的函數(參見定時器實驗),如alt_irq_register()。其清單如下:

#endif /* __ALT_TYPES_H__ */

#ifndef __ALT_IRQ_H__

#define __ALT_IRQ_H__

/*

* alt_irq.h is the nios2 specific implementation of the interrupt controller

* interface.

*/

#include

#include "nios2.h"

#include "alt_types.h"

#ifdef __cplusplus

extern "C"

{

#endif /* __cplusplus */

/*

* Macros used by alt_irq_enabled

*/

#define ALT_IRQ_ENABLED 1

#define ALT_IRQ_DISABLED 0

/*

* number of available interrupts

*/

#define ALT_NIRQ NIOS2_NIRQ

/*

* Used by alt_irq_disable_all() and alt_irq_enable_all().

*/

typedef int alt_irq_context;

/*

* alt_irq_enabled can be called to determine if interrupts are enabled. The

* return value is zero if interrupts are disabled, and non-zero otherwise.

*/

static ALT_INLINE int ALT_ALWAYS_INLINE alt_irq_enabled (void)

{

int status;

NIOS2_READ_STATUS (status);

return status & NIOS2_STATUS_PIE_MSK;

}

/*

* alt_irq_init() is the device initialisation function. This is called at

* config time, before any other driver is initialised.

*/

static ALT_INLINE void ALT_ALWAYS_INLINE

alt_irq_init (const void* base)

{

NIOS2_WRITE_IENABLE (0);

NIOS2_WRITE_STATUS (NIOS2_STATUS_PIE_MSK);

}

/*

* alt_irq_register() can be used to register an interrupt handler. If the

* function is succesful, then the requested interrupt will be enabled upon

* return.

*/

extern int alt_irq_register (alt_u32 id,

void*   context,

void (*irq_handler)(void*, alt_u32));

/*

* alt_irq_disable_all() inhibits all interrupts.

*/

static ALT_INLINE alt_irq_context ALT_ALWAYS_INLINE

alt_irq_disable_all (void)

{

alt_irq_context context;

NIOS2_READ_STATUS (context);

NIOS2_WRITE_STATUS (0);

return context;

}

/*

* alt_irq_enable_all() re-enable all interrupts that currently have registered

* interrupt handlers (and which have not been masked by a call to

* alt_irq_disable()).

*/

static ALT_INLINE void ALT_ALWAYS_INLINE

alt_irq_enable_all (alt_irq_context context)

{

NIOS2_WRITE_STATUS (context);

}

/*

* alt_irq_disable() disables the individual interrupt indicated by "id".

*/

static ALT_INLINE int ALT_ALWAYS_INLINE alt_irq_disable (alt_u32 id)

{

alt_irq_context status;

extern volatile alt_u32 alt_irq_active;

status = alt_irq_disable_all ();

alt_irq_active &= ~(1 << id);

NIOS2_WRITE_IENABLE (alt_irq_active);

alt_irq_enable_all(status);

return 0;

}

/*

* alt_irq_enable() enables the individual interrupt indicated by "id".

*

*/

static ALT_INLINE int ALT_ALWAYS_INLINE alt_irq_enable (alt_u32 id)

{

alt_irq_context status;

extern volatile alt_u32 alt_irq_active;

status = alt_irq_disable_all ();

alt_irq_active |= (1 << id);

NIOS2_WRITE_IENABLE (alt_irq_active);

alt_irq_enable_all(status);

return 0;

}

#ifndef ALT_EXCEPTION_STACK

/*

* alt_irq_initerruptable() should only be called from within an ISR. It is used

* to allow higer priority interrupts to interrupt the current ISR. The input

* argument, "priority", is the priority, i.e. interrupt number of the current

* interrupt.

*

* If this function is called, then the ISR is required to make a call to

* alt_irq_non_interruptible() before returning. The input argument to

* alt_irq_non_interruptible() is the return value from alt_irq_interruptible().

*

* Care should be taken when using this pair of functions, since they increasing

* the system overhead associated with interrupt handling.

*

* If you are using an exception stack then nested interrupts won't work, so

* these functions are not available in that case.

*/

static ALT_INLINE alt_u32 ALT_ALWAYS_INLINE alt_irq_interruptible (alt_u32 priority)

{

extern volatile alt_u32 alt_priority_mask;

extern volatile alt_u32 alt_irq_active;

alt_u32 old_priority;

old_priority      = alt_priority_mask;

alt_priority_mask = (1 << priority) - 1;

NIOS2_WRITE_IENABLE (alt_irq_active & alt_priority_mask);

NIOS2_WRITE_STATUS (1);

return old_priority;

}

/*

* See Comments above for alt_irq_interruptible() for an explanation of the use of this

* function.

*/

static ALT_INLINE void ALT_ALWAYS_INLINE alt_irq_non_interruptible (alt_u32 mask)

{

extern volatile alt_u32 alt_priority_mask;

extern volatile alt_u32 alt_irq_active;

NIOS2_WRITE_STATUS (0);

alt_priority_mask = mask;

NIOS2_WRITE_IENABLE (mask & alt_irq_active);

}

#endif

/*

* alt_irq_pending() returns a bit list of the current pending interrupts.

* This is used by alt_irq_handler() to determine which registered interrupt

* handlers should be called.

*/

static ALT_INLINE alt_u32 ALT_ALWAYS_INLINE alt_irq_pending (void)

{

alt_u32 active;

NIOS2_READ_IPENDING (active);

return active;

}

#ifdef __cplusplus

}

#endif /* __cplusplus */

#endif /* __ALT_IRQ_H__ */

13.priv/alt_busy_sleep.h

priv/alt_busy_sleep.h位於路徑C:"altera"72"nios2eds"components"altera_nios2"HAL"inc"priv下。它定義了延時函數alt_busy_sleep。其清單如下:

#ifndef __ALT_BUSY_SLEEP_H

#define __ALT_BUSY_SLEEP_H

/*

* The function alt_busy_sleep provides a busy loop implementation of usleep.

* This is used to provide usleep for the standalone HAL, or when the timer is

* unavailable in uC/OS-II.

*/

extern unsigned int alt_busy_sleep (unsigned int us);

#endif /* __ALT_BUSY_SLEEP_H */

14.參考文獻

[1] 周立功,等. SOPC嵌入式系統實驗教程(一)[M]. 北京:北京航空航天大學出版社,2006.

[2] 李蘭英,等. Nios II 嵌入式軟核 SOPC設計原理及應用[M]. 北京:北京航空航天大學出版社,2006.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值