Fujitser 0 20 июля, 2017 Опубликовано 20 июля, 2017 (изменено) · Жалоба Не могу сделать простой пример работы с прерываниями 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 } Изменено 21 июля, 2017 пользователем IgorKossak [codebox] для длинного кода. [code]-для короткого!!! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Jury093 2 20 июля, 2017 Опубликовано 20 июля, 2017 · Жалоба 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); в средней строке во втором аргументе на один нолик больше - так надо? судя по имени регистра - это настройка типа прерывания и явно не для того пина.. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Fujitser 0 21 июля, 2017 Опубликовано 21 июля, 2017 · Жалоба в средней строке во втором аргументе на один нолик больше - так надо? судя по имени регистра - это настройка типа прерывания и явно не для того пина.. Спасибо, что заметили (где-то при экспериментах случайно вставил лишний нолик), но увы, дело не в этом. Всё равно не работает. Такое ощущение, что не сбрасывается статус прерывания в обработчике: alt_gpio_port_int_status_clear(ALT_GPIO_PORTC, 0x00200000); и прерывание вызывается снова и снова. При этом сама функция alt_gpio_port_int_status_clear возвращает 0, т.е. она отработала без ошибок. Какие-нибудь идеи ещё есть? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Jury093 2 21 июля, 2017 Опубликовано 21 июля, 2017 · Жалоба Спасибо, что заметили (где-то при экспериментах случайно вставил лишний нолик), но увы, дело не в этом. Такое ощущение, что не сбрасывается статус прерывания в обработчике: Какие-нибудь идеи ещё есть? насколько я помню работу в линуксе с HPS, то прерывания с edge вполне работали.. попробуйте вместо кнопки подвести сигнал с нормальными фронтами и сгенерить одиночный импульс и посмотреть на поведение программы.. вы сейчас кормите прерывание пачкой импульсов и установка alt_gpio_port_debounce_set вам не поможет Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 21 июля, 2017 Опубликовано 21 июля, 2017 · Жалоба Вероятно, здесь ошибка в коде 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. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Fujitser 0 21 июля, 2017 Опубликовано 21 июля, 2017 · Жалоба установка alt_gpio_port_debounce_set вам не поможет Поможет, если выставить частоту gpio_db_clk: alt_clk_divider_set(ALT_CLK_GPIO_DB, 1 << 17); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Fujitser 0 21 июля, 2017 Опубликовано 21 июля, 2017 · Жалоба Как видно, запись идёт в регистр статуса 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 это подтверждает. Видимо, из-за этого и возникла путаница. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться