2014/03/20

Nios II 시작해보자-5(PIO Interrupt)

이번엔 PIO 인터럽트를 사용해보자.

유저 인터페이스로 12분할 엔코더와 스위치를 달아놨는데 함 써먹어 봐야겠어.


엔코더는 A, B signal 외에 스위치 시그널도 있다. 푸쉬타입을 샀거등...

그 외 버튼이 두개가 있고 이걸 interface_Logic에 연결해서 입력신호 필터링을 한 후

인터럽트 신호와 무슨 신호인지 알려주는 데이터 신호가 출력된다.


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity Interface_Logic_v001 is
port
(
clk_in : in std_logic;
EN0_S : in std_logic;
EN0_A, EN0_B : in std_logic;
Key_01, Key_02 : in std_logic;

data : out std_logic_vector(2 downto 0);
intr : out std_logic
);
end entity;

architecture rtl of Interface_Logic_v001 is
signal data_signal : std_logic_vector(2 downto 0);
signal ff_EN0_A, ff_EN0_S, ff_Key_01, ff_Key_02 : std_logic_vector(1 downto 0);
signal rotary_in : std_logic_vector(1 downto 0);
signal rotary_q1, rotary_q2 : std_logic;
begin
process(clk_in)
variable clk_cnt_ENR, clk_cnt_ENL, clk_cnt : integer := 0;
variable phase : integer := 0;
begin
if(clk_in 'event and clk_in = '1') then
if(phase = 0) then
ff_EN0_A(0) <= rotary_q1;
ff_EN0_A(1) <= ff_EN0_A(0);

ff_EN0_S(0) <= EN0_S;
ff_EN0_S(1) <= ff_EN0_S(0);

ff_Key_01(0) <= Key_01;
ff_Key_01(1) <= ff_Key_01(0);

ff_Key_02(0) <= Key_02;
ff_Key_02(1) <= ff_Key_02(0);

if(ff_EN0_A(1) = '0' and ff_EN0_A(0) = '1') then
if(rotary_q2 = '0') then
data <= "000";
else
data <= "001";
end if;
phase := 1;
end if;
elsif(phase = 1) then
intr <= '1';
clk_cnt := clk_cnt + 1;
if(clk_cnt = 100) then
clk_cnt := 0;
intr <= '0';
phase := 0;
end if;
end if;
end if;
end process;

process(clk_in)
begin
if(clk_in 'event and clk_in = '1') then
rotary_in <= (not EN0_B) & (not EN0_A);

case rotary_in is
when "00" => rotary_q1 <= '0';
rotary_q2 <= rotary_q2;
when "01" => rotary_q1 <= rotary_q1;
rotary_q2 <= '0';
when "10" => rotary_q1 <= rotary_q1;
rotary_q2 <= '1';
when "11" => rotary_q1 <= '1';
rotary_q2 <= rotary_q2;
when others => rotary_q1 <= rotary_q1;
rotary_q2 <= rotary_q2;
end case;
end if;
end process;
end rtl;

인터페이스 로직은 일단 이렇다. 엔코더만 구현해 놓은 상태...
Syntax Highlighting을 어떻게 하는지 몰라서 보기 좀 불편하네.

엔코더를 오른쪽으로 돌리면 data 핀으로 "000"가 출력되고, 왼쪽으로 돌리면 "001"이 출력된다. 그리고 인터럽트 핀에서 한 펄스 날라간다. Nios II 에서는 이 인터럽트 시그널을 받아서 인터럽트 처리를 해주면 된다는 시나리온데...잘 되려나...

Qsys 띄워서 시작해보자.






















현재 내 시스템은 이렇다. 인터럽트 입력용으로 사용할 PIO 하나 추가해보자.































이런식으로 설정했다. Rising Edge에 인터럽트가 발생하는 설정이라고 한건데...해봐야 알지 머






















추가했다. 저장하고 제너레이션 해보자.

하드웨어 설정하고 만드는건 별 문제가 없는데...
정작 C 프로그래밍은 어떻게 해야되는지 간단명료하게 설명된 문서를 찾기가 너무 힘드네...

결국 Nios II Software Developer's Handbook에서 PIO Interrupt 관련 예제를 찾았다.
인터넷 뒤진다고 눈깔 빠지는줄 알았다.

우선 ISR 함수 만들고, PIO 인터럽트로 등록해주는 소스다.

#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "alt_types.h"
#ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT
static void handle_button_interrupts(void* context)
#else
static void handle_button_interrupts(void* context, alt_u32 id)
#endif
{
/* Cast context to edge_capture's type. It is important that this
be declared volatile to avoid unwanted compiler optimization. */
volatile int* edge_capture_ptr = (volatile int*) context;
/*
* Read the edge capture register on the button PIO.
* Store value.
*/
*edge_capture_ptr =
IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE);
/* Write to the edge capture register to reset it. */
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0);
/* Read the PIO to delay ISR exit. This is done to prevent a
spurious interrupt in systems with high processor -> pio
latency and fast interrupts. */
IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE);
}


#include "sys/alt_irq.h"
#include "system.h"
...
/* Declare a global variable to hold the edge capture value. */
volatile int edge_capture;
...
/* Initialize the button_pio. */
static void init_button_pio()
{
/* Recast the edge_capture pointer to match the
alt_irq_register() function prototype. */
void* edge_capture_ptr = (void*) &edge_capture;
/* Enable all 4 button interrupts. */
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf);
/* Reset the edge capture register. */
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0x0);
/* Register the ISR. */
#ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT
alt_ic_isr_register(BUTTON_PIO_IRQ_INTERRUPT_CONTROLLER_ID,
BUTTON_PIO_IRQ,
handle_button_interrupts,
edge_capture_ptr, 0x0);
#else
alt_irq_register( BUTTON_PIO_IRQ,
edge_capture_ptr,
handle_button_interrupts );
#endif
}

소스 찾기 드럽게 힘드네...


이걸 베이스로 해서 일단 인터럽트가 발생하면 '.'을 찍어서 제대로 돌아가는지 확인해보자.

static void handle_UI_interrupts(void* context){
volatile int* edge_capture_ptr = (volatile int*) context;

*edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(PIO_3_BASE);

alt_putchar('.');

IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_3_BASE, 0);
IORD_ALTERA_AVALON_PIO_EDGE_CAP(PIO_3_BASE);
}

이렇게 해서 돌려봤다.
























엔코더를 좌우로 돌리면 화면상에 ......이 출력된다.

흠,,, 되는거 같다.

여기에 좀 더 살을 붙여서 유저 인터페이스를 완성해야겠다.


PIO 인터럽트 하는김에 UART도 인터럽트 형태로 바꿀려고 했는데...
도저히 샘플 소스를 찾을 수가 없다...ㅠㅠ
나중에 한가해지면 핸드북 꼼꼼하게 읽어보면서 바꿀 생각이다.

오늘은 여기까지.