Перейти к содержанию
    

Cyclone V baremetal прерывания

Не могу сделать простой пример работы с прерываниями GPIO. Процессор Altera Cyclone V (ARM Cortex A9). Приложение bare-metal.

 

К порту С подключены кнопки.

Делаю прерывание по уровню, вроде бы работает, при нажатии на кнопку вызывается ISR, и вызывается непрерывно, пока кнопка нажата. Если я пытаюсь сделать прерывания по фронту, то после первого нажатия на кнопку ISR вызывается, и продолжает вызываться непрерывно после этого.

 

Уже перепробовал все комбинации уровень/фронт и полярность. Ошибка явно в чём-то другом

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include “alt_interrupt.h”
#include “alt_timers.h”
#include “alt_generalpurpose_io.h”

	volatile bool blink = false;

	/******************************************************************************/
	/*!
	 * ISR Callback
	 *
	 * \param	   icciar
	 * \param	   context ISR context.
	 * \return	  none
	 */
	static void gpio_isr_callback() {

		long mask = alt_gpio_port_int_status_get(ALT_GPIO_PORTC);
		// Clear interrupt source don't care about the return value
		long status = alt_gpio_port_int_status_clear(ALT_GPIO_PORTC, 0x00200000);
		mask = alt_gpio_port_int_status_get(ALT_GPIO_PORTC);
		alt_gpio_port_data_write(ALT_GPIO_PORTB, 0x0F000000, blink << 24);
		blink = !blink;
	}

	/******************************************************************************/
	/*!
	 * Main entry point
	 *
	 */
	int main(void) {
		alt_gpio_init();
		alt_gpio_port_datadir_set(ALT_GPIO_PORTB, 0x0F000000, 0x0F000000);
			// System init
			alt_gpt_all_tmr_init();
			// Setup Interrupt
		alt_int_global_init();
			// Initialize CPU interrupts
			alt_int_cpu_init();
			// Set interrupt distributor target
			int cpu_target = 0x1; //CPU0 will handle the interrupts
			alt_int_dist_target_set(ALT_INT_INTERRUPT_GPIO2, cpu_target);
			// Set interrupt trigger type
			alt_int_dist_trigger_set(ALT_INT_INTERRUPT_GPIO2, ALT_INT_TRIGGER_EDGE);
			// Enable interrupt at the distributor level
			alt_int_dist_enable(ALT_INT_INTERRUPT_GPIO2);
			// Enable CPU interrupts
			alt_int_cpu_enable();
			// Enable global interrupts
			alt_int_global_enable();

			alt_gpio_port_datadir_set(ALT_GPIO_PORTC, 0x01e00000, 0x00000000);
		alt_gpio_port_debounce_set(ALT_GPIO_PORTC, 0x01e00000,	0x00200000);
		alt_gpio_port_int_type_set(ALT_GPIO_PORTC, 0x01e00000,	   0x00200000);
		alt_gpio_port_int_pol_set(ALT_GPIO_PORTC, 0x01e00000,		 0x00200000);

		// Register gpio ISR
		alt_int_isr_register(ALT_INT_INTERRUPT_GPIO2, gpio_isr_callback, NULL);
		alt_gpio_port_int_enable(ALT_GPIO_PORTC, 0x00200000);

		while(1);
		return 0; //unreachable
	}

Изменено пользователем IgorKossak
[codebox] для длинного кода. [code]-для короткого!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

alt_gpio_port_debounce_set(ALT_GPIO_PORTC, 0x01e00000, 0x00200000);

alt_gpio_port_int_type_set(ALT_GPIO_PORTC, 0x01e00000, 0x002000000);

alt_gpio_port_int_pol_set(ALT_GPIO_PORTC, 0x01e00000, 0x00200000);

в средней строке во втором аргументе на один нолик больше - так надо? судя по имени регистра - это настройка типа прерывания и явно не для того пина..

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в средней строке во втором аргументе на один нолик больше - так надо? судя по имени регистра - это настройка типа прерывания и явно не для того пина..

 

Спасибо, что заметили (где-то при экспериментах случайно вставил лишний нолик), но увы, дело не в этом.

Всё равно не работает.

Такое ощущение, что не сбрасывается статус прерывания в обработчике:

alt_gpio_port_int_status_clear(ALT_GPIO_PORTC, 0x00200000);

и прерывание вызывается снова и снова. При этом сама функция alt_gpio_port_int_status_clear возвращает 0, т.е. она отработала без ошибок.

 

Какие-нибудь идеи ещё есть?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, что заметили (где-то при экспериментах случайно вставил лишний нолик), но увы, дело не в этом.

Такое ощущение, что не сбрасывается статус прерывания в обработчике:

Какие-нибудь идеи ещё есть?

насколько я помню работу в линуксе с HPS, то прерывания с edge вполне работали..

попробуйте вместо кнопки подвести сигнал с нормальными фронтами и сгенерить одиночный импульс и посмотреть на поведение программы..

вы сейчас кормите прерывание пачкой импульсов и установка alt_gpio_port_debounce_set вам не поможет

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вероятно, здесь ошибка в коде hwlib библиотеки от Альтеры.

Посмотрите код функции сброса флага прерывания alt_gpio_port_int_status_clear:

/********************************************************************************
********/
/* Clear the interrupt pending status of selected signals of the specified GPIO         */
/* register.                                                                            */
/********************************************************************************
********/

ALT_STATUS_CODE alt_gpio_port_int_status_clear(ALT_GPIO_PORT_t gpio_pid,
        uint32_t clrmask)
{
    volatile uint32_t   *addr;

    if (clrmask & ~ALT_GPIO_BITMASK)      { return ALT_E_ERROR; }
    if (gpio_pid == ALT_GPIO_PORTA)      { addr = ALT_GPIO0_INTSTAT_ADDR; }
    else if (gpio_pid == ALT_GPIO_PORTB) { addr = ALT_GPIO1_INTSTAT_ADDR; }
    else if (gpio_pid == ALT_GPIO_PORTC) { addr = ALT_GPIO2_INTSTAT_ADDR; }
    else { return ALT_E_BAD_ARG; }         /* argument error */

    alt_write_word(addr, clrmask);
    return ALT_E_SUCCESS;
}

Как видно, запись идёт в регистр статуса gpio_intstatus, который вообще read only и никак не может сбросить флаг.

Правильный регистр - gpio_porta_eoi.

Исправьте код функции, заменив для всех портов адрес ALT_GPIOx_INTSTAT_ADDR на ALT_GPIOx_PORTA_EOI_ADDR.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

установка alt_gpio_port_debounce_set вам не поможет

 

Поможет, если выставить частоту gpio_db_clk:

alt_clk_divider_set(ALT_CLK_GPIO_DB, 1 << 17);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как видно, запись идёт в регистр статуса gpio_intstatus, который вообще read only и никак не может сбросить флаг.

Правильный регистр - gpio_porta_eoi.

Исправьте код функции, заменив для всех портов адрес ALT_GPIOx_INTSTAT_ADDR на ALT_GPIOx_PORTA_EOI_ADDR.

 

Спасибо, помогло!

Кстати, прерывания таймера сбрасываются именно чтением из регистра TMRSEOI:

alt_read_word(ALT_OSC1TMR1_TMRSEOI_ADDR);

и TRM это подтверждает. Видимо, из-за этого и возникла путаница.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...