Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Nios II Uart прерывания по приему байта
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Системы на ПЛИС - System on a Programmable Chip (SoPC)
Acvarif
Организованы в системе на Nios II прерыывания Uart по приему одного байта на скорости 115200
Байты приходят короткими пакетами по 72 байта с периодом 0.5 сек. Если количество принятых байт равно
72 и первый байт равен 0х55 то Start = 1; В основном цикле main все сбрасывается (uart_count, Start).
...
Код
alt_u8 Start = 0;
alt_u8 buf_uart[256];
alt_u16 uart_count = 0;
...
// Инициализация прерываний по UART
void Uart_irq_init()
{
    // Регистрация обработчика прерываний
    alt_irq_register(UART_0_IRQ, (void*)UART_0_IRQ, uart_isr);
}
...
// прерывания по UART
static void uart_isr(void* context, alt_u32 id)
{
  alt_u32 status;
  // Чтение регистра статуса для определения причины прерываний
  status = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
  // Очистка всех флагов ошибок
  IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0);
  // Процесс чтения irq
  if (status&ALTERA_AVALON_UART_STATUS_RRDY_MSK)
  {
    buf_uart[uart_count] = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);
    uart_count++;
  }
  if(uart_count == 72 && buf_uart[0] == 0x55)
  {
    Start = 1;
    uart_count = 0;
  }
}

Main
Код
int main (void)
{
    alt_u8 led = 0x2;
    alt_u8 dir = 0;

    Uart_irq_init();
    alt_irq_enabled();

    // главный цикл
  while(1)
  {
    if(Start == 1)
    {
        Start = 0;
        // бегущий светодиод
        if(led & 0x81)
            dir = (dir ^ 0x1);
        if(dir)
            led = led >> 1;
        else
            led = led << 1;
        // запись в PIO_0_BASE (0x00000000) led
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, led);
    }
  }
  return 0;
}


Данный тест проводится на Циклоне 4 (DE0Nano Terasic). Проблема в том, что если количество байт в пакете небольшое (~40) то все работает нормально. С увеличением количества байт в пекете очевидно сбивается счетчик uart_count и первый синхробайт 0x55 уже не определяется. Код простейший. Подскажите пожалуйста что в нем не так?
Acvarif
Немного переделал код. Заработало.
CODE
#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "alt_types.h"
#include "stdio.h"
#include "sys/alt_irq.h"
#include "altera_avalon_uart_regs.h"
#include <unistd.h>
#include <io.h>

//---------------------------------------------------------------------------------------------
// Константы

#define BAUD_RATE 115200

//---------------------------------------------------------------------------------------------
// Объявления функций

void UartIsr(void* context, alt_u16 id);
void InitUart(alt_u32 BaudRate);

//---------------------------------------------------------------------------------------------
// Глобальные переменные

alt_u8 Start = 0;
alt_u8 Buf_uart[256];
alt_u16 Uart_count = 0;

//---------------------------------------------------------------------------------------------
// Функции

//---------------------------------------------------------------------------------------------
// прерывания по UART

void UartIsr(void* context, alt_u16 id)
{
alt_u16 status;
// Чтение регистра статуса для определения причины прерываний
status = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
// Процесс чтения irq
if (status & ALTERA_AVALON_UART_STATUS_RRDY_MSK)
{
Buf_uart[Uart_count] = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);
Uart_count++;
// Очистка всех флагов ошибок
IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0);
}
if(Uart_count >= 72 && Buf_uart[0] == 0x55)
{
Start = 1;
Uart_count = 0;
}
}

//---------------------------------------------------------------------------------------------
// инициализация Uart

void InitUart(alt_u32 BaudRate)
{
alt_u16 context_uart;
alt_u16 divisor;

divisor = (ALT_CPU_FREQ/BaudRate) + 1;
IOWR_ALTERA_AVALON_UART_DIVISOR(UART_0_BASE, divisor);
IOWR_ALTERA_AVALON_UART_CONTROL(UART_0_BASE, ALTERA_AVALON_UART_CONTROL_RRDY_MSK);
// регистрация прерываний UART_0_IRQ
alt_irq_register(UART_0_IRQ, &context_uart, UartIsr);
// alt_irq_enable (UART_0_IRQ);
}

//---------------------------------------------------------------------------------------------

int main (void)
{
alt_u8 led = 0x2;
alt_u8 dir = 0;

InitUart(BAUD_RATE);
alt_irq_enabled();

// main while
while(1)
{
if(Start == 1)
{
Start = 0;
// running led
if(led & 0x81)
dir = (dir ^ 0x1);
if(dir)
led = led >> 1;
else
led = led << 1;
// write in PIO_0_BASE led
IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, led);
}
}
return 0;
}

Далее в SOPC в модуле UART убраз птичку с "Fixed baud rate" для возможности программного назначения частоты бод. Код (или SOPC система) не работает. Можно-ли программно корректировать частоту бод в модуле UART Nios?
gridinp
в статусе надо ещё проверять ALTERA_AVALON_UART_STATUS_PE_MSK и ALTERA_AVALON_UART_STATUS_FE_MSK

очистку флагов надо делать в IOWR_ALTERA_AVALON_UART_CONTROL, хотя может адреса со статусом и совпадают, не помню

смена скорости:
IOWR_ALTERA_AVALON_UART_DIVISOR(base, div);
Acvarif
Цитата(gridinp @ Jun 27 2016, 13:05) *
в статусе надо ещё проверять ALTERA_AVALON_UART_STATUS_PE_MSK и ALTERA_AVALON_UART_STATUS_FE_MSK

очистку флагов надо делать в IOWR_ALTERA_AVALON_UART_CONTROL, хотя может адреса со статусом и совпадают, не помню

смена скорости:
IOWR_ALTERA_AVALON_UART_DIVISOR(base, div);

С флагами вроде все нормально. Хотя еще проверю... А вот с установкой скорости все тоже. Не работает.
Код:
Код
alt_u16 context_uart;
    alt_u16 divisor;

    divisor = (ALT_CPU_FREQ/BaudRate) + 1;
    IOWR_ALTERA_AVALON_UART_DIVISOR(UART_0_BASE, divisor);
    IOWR_ALTERA_AVALON_UART_CONTROL(UART_0_BASE, ALTERA_AVALON_UART_CONTROL_RRDY_MSK);

Работатает только при
Код
#define UART_0_FIXED_BAUD 1

Странно. Думал имеется возможность немного корректировать скорость... модуль UART Quartus 11.0 Sp1. Может кто тоже сталкивался с таким казусом?
gridinp
ну в общем, чтобы по пунктам не обьяснять, смотрите в своем bsp файлы:

*_bsp/drivers/src/altera_avalon_uart_*.c

в частности функцию altera_avalon_uart_rxirq
Acvarif
Цитата(gridinp @ Jun 27 2016, 16:40) *
ну в общем, чтобы по пунктам не обьяснять, смотрите в своем bsp файлы:

*_bsp/drivers/src/altera_avalon_uart_*.c

в частности функцию altera_avalon_uart_rxirq

Да, есть такая функция
CODE
/*
* altera_avalon_uart_rxirq() is called by altera_avalon_uart_irq() to
* process a receive interrupt. It transfers the incoming character into
* the receive circular buffer, and sets the apropriate flags to indicate
* that there is data ready to be processed.
*/
static void
altera_avalon_uart_rxirq(altera_avalon_uart_state* sp, alt_u32 status)
{
alt_u32 next;

/* If there was an error, discard the data */

if (status & (ALTERA_AVALON_UART_STATUS_PE_MSK |
ALTERA_AVALON_UART_STATUS_FE_MSK))
{
return;
}

/*
* In a multi-threaded environment, set the read event flag to indicate
* that there is data ready. This is only done if the circular buffer was
* previously empty.
*/

if (sp->rx_end == sp->rx_start)
{
ALT_FLAG_POST (sp->events, ALT_UART_READ_RDY, OS_FLAG_SET);
}

/* Determine which slot to use next in the circular buffer */

next = (sp->rx_end + 1) & ALT_AVALON_UART_BUF_MSK;

/* Transfer data from the device to the circular buffer */

sp->rx_buf[sp->rx_end] = IORD_ALTERA_AVALON_UART_RXDATA(sp->base);

sp->rx_end = next;

next = (sp->rx_end + 1) & ALT_AVALON_UART_BUF_MSK;

/*
* If the cicular buffer was full, disable interrupts. Interrupts will be
* re-enabled when data is removed from the buffer.
*/

if (next == sp->rx_start)
{
sp->ctrl &= ~ALTERA_AVALON_UART_CONTROL_RRDY_MSK;
IOWR_ALTERA_AVALON_UART_CONTROL(sp->base, sp->ctrl);
}
}
Находится в файле altera_avalon_uart_init.c
Как она связана с тем, что uart не работает при софт установке скорости бод
Код
#define UART_0_FIXED_BAUD 0
gridinp
да это про обработку прерываний функция, что нельзя данные забирать, когда ошибка приема

а скорость в другом файле, там надо делить частоту на которой uart работает, ну что там думать
порт на передачу и осциллографом посмотреть
Acvarif
Цитата(gridinp @ Jun 27 2016, 22:46) *
да это про обработку прерываний функция, что нельзя данные забирать, когда ошибка приема

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

Спасибо. Про прием понятно.
Софт установка скорости не работает. Уже не знаю что и думать. Вывел UART_TXD на осциллограф. При #define UART_0_FIXED_BAUD 1 все работает. При #define UART_0_FIXED_BAUD 0 на выходе логическая 1. Ошибка компонента SOPC UART?
gridinp
а, что вы кладете в делитель?

должно быть примерно так:

IOWR_ALTERA_AVALON_UART_DIVISOR(UART_BASE, UART_FREQ / 9600 - 1)
arpa-net
Цитата(Acvarif @ Jun 28 2016, 12:05) *
Спасибо. Про прием понятно.
Софт установка скорости не работает. Уже не знаю что и думать. Вывел UART_TXD на осциллограф. При #define UART_0_FIXED_BAUD 1 все работает. При #define UART_0_FIXED_BAUD 0 на выходе логическая 1. Ошибка компонента SOPC UART?


Вот так делитель считается:
int divisor = (int) ( UART_FREQ / baud + 0.5);
Acvarif
Цитата(arpa-net @ Jun 29 2016, 03:01) *
Вот так делитель считается:
int divisor = (int) ( UART_FREQ / baud + 0.5);

Понятно. Так и делаю. Uart не работает. Работает только при
Код
#define UART_0_FIXED_BAUD 1

И вообще странности.
Вот прием по прерыванию
Код
  // Чтение регистра статуса для определения причины прерываний
  status = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
  // Очистка всех флагов ошибок
  IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0);

  // Процесс чтения irq (если байт получен  бит RRDY = 1)
  if (status & ALTERA_AVALON_UART_STATUS_RRDY_MSK)
  {
      // Чтение данных если нет ошибок приема (PE-ошибка бита паритета FE-некоректный стоп бит)
//      if (!(status & (ALTERA_AVALON_UART_STATUS_PE_MSK | ALTERA_AVALON_UART_STATUS_FE_MSK)))
      if (!(status & (ALTERA_AVALON_UART_STATUS_FE_MSK)))
      {
            BufRxUart[UartRxCount] = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);
            UartRxCount++;
      }
  }

То работает то нет при разной длине входного пакета. Может действительно в Quartus11.0 проблема с Uart компонентом?
gridinp
Цитата(Acvarif @ Jun 29 2016, 13:28) *
То работает то нет при разной длине входного пакета. Может действительно в Quartus11.0 проблема с Uart компонентом?

странно конечно, прежде чем в глубину копать, может попробовать штатным
драйвером прием/передачу
open("/dev/uart0" ...
read, write
ioctl - для смены скорости
arpa-net
Цитата(Acvarif @ Jun 29 2016, 11:28) *
Понятно. Так и делаю. Uart не работает. Работает только при
Код
#define UART_0_FIXED_BAUD 1

И вообще странности.
Вот прием по прерыванию
Код
  // Чтение регистра статуса для определения причины прерываний
  status = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
  // Очистка всех флагов ошибок
  IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0);

  // Процесс чтения irq (если байт получен  бит RRDY = 1)
  if (status & ALTERA_AVALON_UART_STATUS_RRDY_MSK)
  {
      // Чтение данных если нет ошибок приема (PE-ошибка бита паритета FE-некоректный стоп бит)
//      if (!(status & (ALTERA_AVALON_UART_STATUS_PE_MSK | ALTERA_AVALON_UART_STATUS_FE_MSK)))
      if (!(status & (ALTERA_AVALON_UART_STATUS_FE_MSK)))
      {
            BufRxUart[UartRxCount] = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);
            UartRxCount++;
      }
  }

То работает то нет при разной длине входного пакета. Может действительно в Quartus11.0 проблема с Uart компонентом?


Возможно в 11-м квартусе неправильно генерируется bsp. Я бы вам рекомендовал уставновить quartus 15.1 web edition, он бесплатный. Для вашего hello world вполне сойдет. Попробуйте для начала читать и менять скорость c помощью HAL драйверов. Можете загрузить ваш qsys проект, могу глянуть что не так.
Если ваш altera avalon uart не справляется c приемом, то можете использовать fifo uart с altera wiki:
http://www.alterawiki.com/wiki/FIFOed_Avalon_Uart
Он использует теже самые регистры, так что работа с ним ничем не отличается от стандартного. Нужно только скопировать его в папку с альтеровскими ip корками.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.