реклама на сайте
подробности

 
 
> Cyclone V baremetal прерывания
Fujitser
сообщение Jul 20 2017, 15:50
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 294
Регистрация: 28-02-05
Из: Екатеринбург
Пользователь №: 2 925



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

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

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

CODE
#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 - Jul 21 2017, 13:25
Причина редактирования: [codebox] для длинного кода. [code]-для короткого!!!
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов (1 - 6)
Jury093
сообщение Jul 20 2017, 20:15
Сообщение #2


Знающий
****

Группа: Участник
Сообщений: 959
Регистрация: 11-01-06
Из: Санкт-Петербург
Пользователь №: 13 050



Цитата(Fujitser @ Jul 20 2017, 18:50) *
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);

в средней строке во втором аргументе на один нолик больше - так надо? судя по имени регистра - это настройка типа прерывания и явно не для того пина..
Go to the top of the page
 
+Quote Post
Fujitser
сообщение Jul 21 2017, 01:16
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 294
Регистрация: 28-02-05
Из: Екатеринбург
Пользователь №: 2 925



Цитата(Jury093 @ Jul 21 2017, 01:15) *
в средней строке во втором аргументе на один нолик больше - так надо? судя по имени регистра - это настройка типа прерывания и явно не для того пина..


Спасибо, что заметили (где-то при экспериментах случайно вставил лишний нолик), но увы, дело не в этом.
Всё равно не работает.
Такое ощущение, что не сбрасывается статус прерывания в обработчике:
alt_gpio_port_int_status_clear(ALT_GPIO_PORTC, 0x00200000);
и прерывание вызывается снова и снова. При этом сама функция alt_gpio_port_int_status_clear возвращает 0, т.е. она отработала без ошибок.

Какие-нибудь идеи ещё есть?
Go to the top of the page
 
+Quote Post
Jury093
сообщение Jul 21 2017, 08:45
Сообщение #4


Знающий
****

Группа: Участник
Сообщений: 959
Регистрация: 11-01-06
Из: Санкт-Петербург
Пользователь №: 13 050



Цитата(Fujitser @ Jul 21 2017, 04:16) *
Спасибо, что заметили (где-то при экспериментах случайно вставил лишний нолик), но увы, дело не в этом.
Такое ощущение, что не сбрасывается статус прерывания в обработчике:
Какие-нибудь идеи ещё есть?

насколько я помню работу в линуксе с HPS, то прерывания с edge вполне работали..
попробуйте вместо кнопки подвести сигнал с нормальными фронтами и сгенерить одиночный импульс и посмотреть на поведение программы..
вы сейчас кормите прерывание пачкой импульсов и установка alt_gpio_port_debounce_set вам не поможет
Go to the top of the page
 
+Quote Post
sonycman
сообщение Jul 21 2017, 14:40
Сообщение #5


Любитель
*****

Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695



Вероятно, здесь ошибка в коде 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.
Go to the top of the page
 
+Quote Post
Fujitser
сообщение Jul 21 2017, 14:55
Сообщение #6


Местный
***

Группа: Свой
Сообщений: 294
Регистрация: 28-02-05
Из: Екатеринбург
Пользователь №: 2 925



Цитата(Jury093 @ Jul 21 2017, 13:45) *
установка alt_gpio_port_debounce_set вам не поможет


Поможет, если выставить частоту gpio_db_clk:
alt_clk_divider_set(ALT_CLK_GPIO_DB, 1 << 17);
Go to the top of the page
 
+Quote Post
Fujitser
сообщение Jul 21 2017, 17:06
Сообщение #7


Местный
***

Группа: Свой
Сообщений: 294
Регистрация: 28-02-05
Из: Екатеринбург
Пользователь №: 2 925



Цитата(sonycman @ Jul 21 2017, 19:40) *
Как видно, запись идёт в регистр статуса 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 это подтверждает. Видимо, из-за этого и возникла путаница.
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 10:50
Рейтинг@Mail.ru


Страница сгенерированна за 0.01409 секунд с 7
ELECTRONIX ©2004-2016