\documentclass[a4paper]{article}

\input{settings/common-preamble}
\input{settings/bmstu-preamble}
\input{settings/fancy-listings-preamble}
\setcounter{secnumdepth}{0}
\numerationTop

\def\makeyear{2021}
\def\grpNum{11М}

\begin{document}
\fontsize{14}{21}\selectfont
\thispagestyle{empty}
\makeBMSTUHeader

\makeReportTitle{лабораторной}{3}{Проектирование с применением ядра Nios II \\ и ОСРВ $\mu$C/OS-II}{Программное обеспечение встроенных систем}{}{доцент кафедры ИУ-3 \\ Федоров С.В.}
\newpage
\thispagestyle{empty}
\tableofcontents
\newpage
\pagestyle{fancy}
\section{Цель}
Целью работы являлось освоение навыков проектирования программного обеспечения для операционных систем реального времени. Создание простого проекта программного обеспечения системы на кристалле Nios II на основе ОСРВ $\mu$C/OS-II. Изучение основных сервисов ОСРВ $\mu$C/OS-II. Реализация обработчика прерывания в системе на кристалле Nios II. Освоение методики обработки внешних событий в $\mu$C/OS-II.

\section{Выполнение}
\subsection{Часть I. Создание простой системы на основе uC/OS-II}
В первой части работы была создана простая система на основе ОСРВ $\mu$C/OS-II. В приложении \hrf{appendix:pt1full} приведён полный листинг готовой программы. Единственным отличием от предложенной в исходном материале было добавление переключения светодиода по вызову задачи второго семафора, согласно методического материала. В листинге \hrf{lst:pt1change} представлен код, добавленный в тело функции \code{getsem_task2}.
\begin{lstlisting}[language=C, style=CCodeStyle, caption=Внесённые в исходный код изменения, label={lst:pt1change}]
...
INT8U led;
led = 1;
while (1) {
	led = led << 1;
	if (led == 0) led = 1;
   	IOWR_ALTERA_AVALON_PIO_DATA(GREEN_LED_BASE,led);
    ...
\end{lstlisting}
После внесения изменений и компиляции был изучен файл objdump, созданный системой. В листинге \hrf{lst:pt1objdump} приведён фрагмент файла. Исходя из данных в файле (на строках \hrf{line:pt1text} (\code{.text}), \hrf{line:pt1bss} (\code{.bss})), текст программы занял 105240 байт, а для неинициализированных данных было выделено 45288 байт.
\begin{lstlisting}[style=ASMStyle, caption=Фрагмент файла objdump, label={lst:pt1objdump}]
Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .entry        00000020  00000000  00000000  00001000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .exceptions   00000218  00000020  00000020  00001020  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .text         00019b18  00000238  00000238  00001238  2**2<@\label{line:pt1text}@> 
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  3 .rodata       00000d54  00019d50  00019d50  0001ad50  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .rwdata       00001c70  0001aaa4  0001c714  0001baa4  2**2
                  CONTENTS, ALLOC, LOAD, DATA, SMALL_DATA
  5 .bss          0000b0e8  0001e384  0001e384  0001e384  2**2<@\label{line:pt1bss}@>
                  ALLOC, SMALL_DATA
  6 .sdram        00000000  0002946c  0002946c  0001d714  2**0
                  CONTENTS
  7 .instruction_tcm 00000000  10000000  10000000  0001d714  2**0
                  CONTENTS
  8 .comment      0000002d  00000000  00000000  0001d714  2**0
                  CONTENTS, READONLY
\end{lstlisting}

\subsection{Часть II. Реализация обработчиков прерываний в uC/OS-II}
В приложении \hrf{appendix:pt2isr} представлен полный листинг программы, включающий обработку прерываний. Основным отличием является подключение функции обработки прерываний (в листинге \hrf{lst:addisr} представлены фрагмент фукнции инициализации со строкой подключения и функция обработчика). Также, согласно задания, функция \code{send_task} была изменена таким образом, чтобы отправлять сообщения в очередь не по таймеру, а по сигналу от обработчика прерываний.
\begin{lstlisting}[language=C,style=CCodeStyle,label={lst:addisr},caption=Функция обработки прерываний и изменённая функция \code{send_task}]
void isr_button(void* context, alt_u32 id) {
  INT32U value;
  INT8U return_code = OS_ERR_NONE;
  //inform entering isr
  OSIntEnter();
  value = IORD_ALTERA_AVALON_PIO_DATA(BUTTONS_BASE);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE,0);
  //send to queue
  return_code = OSQPostOpt(buttonqueue, (void *)value, OS_POST_OPT_BROADCAST);
  alt_ucosii_check_return_code(return_code);
  IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE);
  //inform leaving isr
  OSIntExit();
}

void send_task(void* pdata) {
  INT8U return_code = OS_ERR_NONE;
  INT32U msg;
  
  while (1) {
      msg = (INT32U) OSQPend(buttonqueue, 0, &return_code);
      if (return_code != OS_ERR_NONE) continue;
      return_code = OSQPostOpt(msgqueue, (void*) msg, OS_POST_OPT_BROADCAST);
      alt_ucosii_check_return_code(return_code);
  }
}

void initialize_task(void* pdata) {
  //...
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTONS_BASE, 0xf);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE, 0x0);
  return_code = alt_irq_register(BUTTONS_IRQ, NULL, isr_button);
  //...
}
\end{lstlisting}

Также для передачи сообщений была создана дополнительная очередь, хранящая сообщения от \code{send_task} к \code{receive_task}'ам.
\begin{lstlisting}[language=C,style=CCodeStyle]
//...
OS_EVENT  *buttonqueue;                    //Message queue pointer
void      *buttonqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages
//...

int initOSDataStructs(void) {
  //...
  buttonqueue = OSQCreate(&buttonqueueTbl[0], MSG_QUEUE_SIZE);
  return 0;
}
\end{lstlisting}


\section{Индивидуальное задание}
К выполнению было выдано следующее индивидуальное задание:

\textbf{\circled{11} Каждую кнопку и семисегментный индикатор обслуживает отдельная задача. После нажатия кнопки соответствующий индикатор начинает моргать с частотой 2 Гц и инкрементироваться каждую секунду, пока кнопка удерживается. Нажатия других кнопок при этом игнорируются. После отпускания кнопки моргание и инкремент прекращаются. }

Для достижения поставленной цели в коде были применены три очереди (для передачи сообщений \textit{от прерывания} по нажатию кнопок к менеджеру сообщений для задач, обслуживающих кнопки, для сообщений \textit{от менеджера} задач непосредственно к задачам, и \textit{для семисегментного} индикатора). В программе реализована следующая логика:
\begin{enumerate}
\item В функции прерывания от кнопок в очередь для сообщений от прерывания отправляется сообщение со значением нажатых кнопок;
\begin{lstlisting}[language=C,style=CCodeStyle]
void isr_button(void* context, alt_u32 id) {
    INT32U value;
    INT8U return_code = OS_ERR_NONE;

    //inform entering isr
    OSIntEnter();

    // read PIO
    value = IORD_ALTERA_AVALON_PIO_DATA(BUTTONS_BASE);
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE,0);

    //send to queue
    <@\lh{red}{return\_code = OSQPostOpt(buttonqueue,}@>
                              <@\lh{red}{(void *)value,}@>
                              <@\lh{red}{OS\_POST\_OPT\_BROADCAST);}@>
    alt_ucosii_check_return_code(return_code);
    IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE);

    //inform leaving isr
    OSIntExit();
}
\end{lstlisting}

\item менеджер очередей ожидает сообщения в очереди прерываний, фильтрует нажатия (игнорирует любые \textit{комбинации} нажатых кнопок) и отправляет два сообщения: в очередь для кнопок (значения нажатых кнопок) и в очередь семисегментного индикатора (служебное сообщение о необходимости сбросить счётчик);
\begin{lstlisting}[language=C,style=CCodeStyle]
void task_manage(void* pdata) {
  INT8U return_code = OS_ERR_NONE;
  INT32U msg;
  
  while (1) {
	  //when a message from ISR is pending
      msg = (INT32U) OSQPend(buttonqueue, 0, &return_code);
      if (return_code != OS_ERR_NONE) continue;
      // filter if more than one button is pressed and send to q
      if (msg == BTN_1_IRQ || msg == BTN_2_IRQ || msg == BTN_3_IRQ || msg == BTN_4_IRQ) {
          return_code = OSQPostOpt(msgqueue, (void*) msg, OS_POST_OPT_BROADCAST);
          alt_ucosii_check_return_code(return_code);
          return_code = OSQPostOpt(ssegqueue, (void*) SEV_SEG_RESET, OS_POST_OPT_BROADCAST);
      	  alt_ucosii_check_return_code(return_code);
      }
  }
}
\end{lstlisting}

\item Каждая задача, обслуживающая кнопки ожидает сообщения в своей очереди, читает его и формирует сообщение в очередь для семисегментного индикатора.
\begin{lstlisting}[language=C,style=CCodeStyle]
void task_btn1(void* pdata) {
    INT8U return_code = OS_ERR_NONE;
    INT32U msg = 0;
  
    while (1) {
	    // read from q
        msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
        alt_ucosii_check_return_code(return_code);
        // if it's my btn - send to sseg task a shift for counter
        if (msg == BTN_1_IRQ) { //<@\lh{dkgreen}{аналогично для BTN 2, 3, 4}@>
        	return_code = OSQPostOpt(ssegqueue, (void*) BTN_1_SHIFT, OS_POST_OPT_BROADCAST);
        	alt_ucosii_check_return_code(return_code);
        }
    }
}
\end{lstlisting}

\item Таймер включается при инициализации и работает постоянно, вызывая функцию обработки прерываний два раза в секунду. В обработчике таймера в очередь семисегментного индикатора кладётся сообщение о факте срабатывания таймера.
\begin{lstlisting}[language=C,style=CCodeStyle]
void timer2Hz(void* ptmr, void* args) {
	INT8U return_code = OS_ERR_NONE;
	return_code = OSQPostOpt(ssegqueue, (void *)SEV_SEG_SWITCH, OS_POST_OPT_BROADCAST);
	alt_ucosii_check_return_code(return_code);
}
\end{lstlisting}

\item Задача, обслуживающая семисегментный индикатор ожидает сообщения в своей очереди и выключает индикатор или записывает в него предыдущее значение. Также задача перед записью (два раза в секунду) проверяет регистр прерываний от \code{BUTTONS_PIO} (не нажата ли какая-то кнопка): если нажата -- каждое второе срабатывание таймера инкрементирует предыдущее запомненное значение, а если не нажата -- выставляет на флагах нули, разрешая таким образом обработчику прерываний от кнопок отправку сообщений в свою очередь и выставляет в счётчике 0;
\begin{lstlisting}[language=C,style=CCodeStyle]
void task_sseg(void *pdata) {
    INT8U return_code = OS_ERR_NONE;
	INT32U msg;
	INT32U buttons;
	INT32U value = 0;
	INT32U multiplier;

	while (1) {
		//pend q
	    msg = (INT32U)OSQPend(ssegqueue, 0, &return_code);
		alt_ucosii_check_return_code(return_code);
		// is something pressed?
		buttons = IORD_ALTERA_AVALON_PIO_DATA(BUTTONS_BASE);
		// if only one is pressed
		if (buttons != 15) {
			// if msg from timer = switch visiblity
			switch (msg) {
			case SEV_SEG_SWITCH:
				isOn = ~isOn;
				break;
			case SEV_SEG_RESET:
				value = 0;
				break;
			case BTN_1_SHIFT:
			case BTN_2_SHIFT:
			case BTN_3_SHIFT:
			case BTN_4_SHIFT:
				multiplier = msg;
				break;
			default:
				printf("something went very wrong!");
			}

			if (isOn) {
				IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_BASE, -1);
				value++;
			} else {
				sevenseg_set_hex((value & 15) * multiplier);
			}
		}
	}
}
\end{lstlisting}
\end{enumerate}

Также была незначительно доработана функция вывода данных на семисегментный индикатор (расширена для использования всех четырёх индикаторов).
\begin{lstlisting}[language=C,style=CCodeStyle]
static void sevenseg_set_hex(int hex) {
  static alt_u8 segments[16] = {
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, /* 0-9 */
    0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E };                       /* a-f */

  unsigned int data = segments[hex & 15] | (segments[(hex >> 4) & 15] << 8) |
		  (segments[(hex >> 8) & 15] << 16) | (segments[(hex >> 12) & 15] << 24);

  IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_BASE, data);
}
\end{lstlisting}

Полный листинг программы, решающей задачу представлен в приложении \hrf{appendix:indi}.

\newpage
\appendix
\setcounter{secnumdepth}{5}
\section*{Приложения}
\addcontentsline{toc}{section}{Приложения}
\renewcommand{\thesubsection}{\Alph{subsection}}

\subsection{Полный листинг программы для простой системы}
\label{appendix:pt1full}
\begin{lstlisting}[language=C,style=CCodeStyle]
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "includes.h"
#include "alt_ucosii_simple_error_check.h"
#include "altera_avalon_pio_regs.h"

/* Ñòåêè çàäà÷ */
#define   TASK_STACKSIZE       1024
OS_STK    initialize_task_stk[TASK_STACKSIZE];
OS_STK    print_status_task_stk[TASK_STACKSIZE];
OS_STK    getsem_task1_stk[TASK_STACKSIZE];
OS_STK    getsem_task2_stk[TASK_STACKSIZE];
OS_STK    receive_task1_stk[TASK_STACKSIZE];
OS_STK    receive_task2_stk[TASK_STACKSIZE];
OS_STK    send_task_stk[TASK_STACKSIZE];

/* Ïðèîðèòåòû çàäà÷ */
#define INITIALIZE_TASK_PRIORITY   6
#define PRINT_STATUS_TASK_PRIORITY 7
#define GETSEM_TASK1_PRIORITY      8
#define GETSEM_TASK2_PRIORITY      9
#define RECEIVE_TASK1_PRIORITY    10
#define RECEIVE_TASK2_PRIORITY    11
#define SEND_TASK_PRIORITY        12


/* Îáúÿâëåíèå î÷åðåäè */
#define   MSG_QUEUE_SIZE  30            //Size of message queue used in example
OS_EVENT  *msgqueue;                    //Message queue pointer 
void      *msgqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages


/* Îáúÿâëåíèå ñåìàôîðà */
OS_EVENT *shared_resource_sem;

/* Ãëîáàëüíûå ïåðåìåííûå */
INT32U number_of_messages_sent = 0;
INT32U number_of_messages_received_task1 = 0;
INT32U number_of_messages_received_task2 = 0;
INT32U getsem_task1_got_sem = 0;
INT32U getsem_task2_got_sem = 0;
char sem_owner_task_name[20];

/* Ïðîòîòèïû ôóíêöèé */
int initOSDataStructs(void);
int initCreateTasks(void);

void print_status_task(void* pdata)
{
  while (1)
  { 
    OSTimeDlyHMSM(0, 0, 3, 0);
    printf("****************************************************************\n");
    printf("Hello From MicroC/OS-II Running on NIOS II.  Here is the status:\n");
    printf("\n");
    printf("The number of messages sent by the send_task:         %lu\n",
            number_of_messages_sent);
    printf("\n");
    printf("The number of messages received by the receive_task1: %lu\n",
            number_of_messages_received_task1);
    printf("\n");
    printf("The number of messages received by the receive_task2: %lu\n",
            number_of_messages_received_task2);
    printf("\n");
    printf("The shared resource is owned by: %s\n",
           &sem_owner_task_name[0]);
    printf("\n");
    printf("The Number of times getsem_task1 acquired the semaphore %lu\n",
            getsem_task1_got_sem);
    printf("\n");
    printf("The Number of times getsem_task2 acquired the semaphore %lu\n",
            getsem_task2_got_sem);
    printf("\n");
    printf("****************************************************************\n");
    printf("\n");
  }
}

void getsem_task1(void* pdata)
{
  INT8U return_code = OS_NO_ERR;
  
  while (1)
  {
    OSSemPend(shared_resource_sem, 0, &return_code);
    alt_ucosii_check_return_code(return_code);
    strcpy(&sem_owner_task_name[0], "getsem_task1");
    getsem_task1_got_sem++;
    OSSemPost(shared_resource_sem);
    OSTimeDlyHMSM(0, 0, 0, 100);
  }
}

void getsem_task2(void* pdata)
{
  INT8U return_code = OS_NO_ERR;
  INT8U led;
  led=1;
  while (1)
  {
    led = led << 1;
	if (led == 0) led = 1;
	IOWR_ALTERA_AVALON_PIO_DATA(GREEN_LED_BASE,led);

	OSSemPend(shared_resource_sem, 0, &return_code);
    strcpy(&sem_owner_task_name[0], "getsem_task2");
    alt_ucosii_check_return_code(return_code);
    getsem_task2_got_sem++;
    OSSemPost(shared_resource_sem);
    OSTimeDlyHMSM(0, 0, 0, 130);
  }
}

void send_task(void* pdata)
{
  INT8U return_code = OS_NO_ERR;
  INT32U  msg = 0;
  OS_Q_DATA queue_data;  
  
  while (1)
  {
    return_code = OSQQuery(msgqueue, &queue_data);
    alt_ucosii_check_return_code(return_code);
    if(queue_data.OSNMsgs < MSG_QUEUE_SIZE) 
    {                                       
      return_code = OSQPostOpt(msgqueue, (void *)&msg, OS_POST_OPT_BROADCAST);
      alt_ucosii_check_return_code(return_code);
      msg++;
      number_of_messages_sent++;
    }
    else
    {
      OSTimeDlyHMSM(0, 0, 1, 0);
    }
  }
}

void receive_task1(void* pdata)
{
  INT8U return_code = OS_NO_ERR;
  INT32U *msg;
  
  while (1)
  {
    msg = (INT32U *)OSQPend(msgqueue, 0, &return_code);
    alt_ucosii_check_return_code(return_code);
    number_of_messages_received_task1++;
    OSTimeDlyHMSM(0, 0, 0, 333);
  }
}

void receive_task2(void* pdata)
{
  INT8U return_code = OS_NO_ERR;
  INT32U *msg;
  
  while (1)
  {
    msg = (INT32U *)OSQPend(msgqueue, 0, &return_code);
    alt_ucosii_check_return_code(return_code);
    number_of_messages_received_task2++;
    OSTimeDlyHMSM(0, 0, 1, 0);
  }
}

void  initialize_task(void* pdata)
{
  INT8U return_code = OS_NO_ERR;
    
  initOSDataStructs();  

  initCreateTasks();
  
  return_code = OSTaskDel(OS_PRIO_SELF);
  alt_ucosii_check_return_code(return_code);
  while (1);
}

int main (int argc, char* argv[], char* envp[])
{
  INT8U return_code = OS_NO_ERR;
  return_code = OSTaskCreate(initialize_task,
                             NULL,
                             (void *)&initialize_task_stk[TASK_STACKSIZE-1],
                             INITIALIZE_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  OSStart();
  return 0;
}

int initOSDataStructs(void)
{
  msgqueue = OSQCreate(&msgqueueTbl[0], MSG_QUEUE_SIZE);
  shared_resource_sem = OSSemCreate(1);
  return 0;
}
 
int initCreateTasks(void)
{
  INT8U return_code = OS_NO_ERR;

  return_code = OSTaskCreate(getsem_task1,
                             NULL,
                             (void *)&getsem_task1_stk[TASK_STACKSIZE-1],
                             GETSEM_TASK1_PRIORITY);
  return_code = OSTaskCreate(getsem_task2,
                             NULL,
                             (void *)&getsem_task2_stk[TASK_STACKSIZE-1],
                             GETSEM_TASK2_PRIORITY);
  return_code = OSTaskCreate(receive_task1,
                             NULL,
                             (void *)&receive_task1_stk[TASK_STACKSIZE-1],
                             RECEIVE_TASK1_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(receive_task2,
                             NULL,
                             (void *)&receive_task2_stk[TASK_STACKSIZE-1],
                             RECEIVE_TASK2_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(send_task,
                             NULL,
                             (void *)&send_task_stk[TASK_STACKSIZE-1],
                             SEND_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(print_status_task,
                             NULL,
                             (void *)&print_status_task_stk[TASK_STACKSIZE-1],
                             PRINT_STATUS_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return 0;
}
\end{lstlisting}

\subsection{Полный листинг программы с обработчиком прерываний}
\label{appendix:pt2isr}
\begin{lstlisting}[language=C,style=CCodeStyle]
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "includes.h"
#include "alt_ucosii_simple_error_check.h"
#include "altera_avalon_pio_regs.h"

// <@\lh{dkgreen}{Стеки задач}@>
#define   TASK_STACKSIZE       1024
OS_STK    initialize_task_stk[TASK_STACKSIZE];
OS_STK    print_status_task_stk[TASK_STACKSIZE];
OS_STK    getsem_task1_stk[TASK_STACKSIZE];
OS_STK    getsem_task2_stk[TASK_STACKSIZE];
OS_STK    receive_task1_stk[TASK_STACKSIZE];
OS_STK    receive_task2_stk[TASK_STACKSIZE];
OS_STK    send_task_stk[TASK_STACKSIZE];

// <@\lh{dkgreen}{Приоритеты задач}@>
#define INITIALIZE_TASK_PRIORITY   6
#define PRINT_STATUS_TASK_PRIORITY 7
#define GETSEM_TASK1_PRIORITY      8
#define GETSEM_TASK2_PRIORITY      9
#define RECEIVE_TASK1_PRIORITY    10
#define RECEIVE_TASK2_PRIORITY    11
#define SEND_TASK_PRIORITY        12


// <@\lh{dkgreen}{Объявление очереди}@>
#define   MSG_QUEUE_SIZE  30   /*Size of message queue used in example*/
OS_EVENT  *msgqueue;                    //Message queue pointer 
void      *msgqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages
OS_EVENT  *buttonqueue;                    //Message queue pointer
void      *buttonqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages
int        btnLog[32];
int        btnLogPtr = 0;

// <@\lh{dkgreen}{Объявление семафора}@>
OS_EVENT *shared_resource_sem;

// <@\lh{dkgreen}{Глобальные переменные}@>
INT32U number_of_messages_sent = 0;
INT32U number_of_messages_received_task1 = 0;
INT32U number_of_messages_received_task2 = 0;
INT32U getsem_task1_got_sem = 0;
INT32U getsem_task2_got_sem = 0;
char sem_owner_task_name[20];

// <@\lh{dkgreen}{Прототипы функций}@>
int initOSDataStructs(void);
int initCreateTasks(void);

static void sevenseg_set_hex(int hex) {
  static alt_u8 segments[16] = {
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, //0-9
    0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E };                       //a-f

  unsigned data = segments[hex & 15] | (segments[(hex > > 4)&15] << 8);
  IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_BASE, data);
}

void print_status_task(void* pdata) {
  INT8U retCode = OS_ERR_NONE;
  INT8U bretCode = OS_ERR_NONE;
  OS_Q_DATA qData;
  OS_Q_DATA qbData;

  while (1) { 
    OSTimeDlyHMSM(0, 0, 10, 0);
    printf("TIME: %10lu sec\n", OSTimeGet() / OS_TICKS_PER_SEC);
    retCode = OSQQuery(msgqueue, &qData);
    if (retCode == OS_ERR_NONE) {
      printf("MESSAGES: %7i\n", qData.OSNMsgs);
    } else {
      printf("QUEUE_ERR: %6i\n", retCode);
    }
    int p;
    for (p = 0; p < 32; ++p)
    	printf("%d, ", btnLog[p]);
    printf("\n");
    for (p = 0; p < 32; ++p)
    	btnLog[p] = 0;
    btnLogPtr = 0;

    bretCode = OSQQuery(buttonqueue, &qbData);
    if (bretCode == OS_ERR_NONE) {
      printf("BTNS MSGS: %7i\n", qbData.OSNMsgs);
    } else {
      printf("QUEUE_ERR: %6i\n", bretCode);
    }
    printf("****************************************************************\n");
    printf("Hello From MicroC/OS-II Running on NIOS II.  Here is the status:\n");
    printf("The number of messages sent by the send_task:         %lu\n",
            number_of_messages_sent);
    printf("The number of messages received by the receive_task1: %lu\n",
            number_of_messages_received_task1);
    printf("The number of messages received by the receive_task2: %lu\n",
            number_of_messages_received_task2);
    printf("The shared resource is owned by: %s\n",
           &sem_owner_task_name[0]);
    printf("The Number of times getsem_task1 acquired the semaphore %lu\n",
            getsem_task1_got_sem);
    printf("The Number of times getsem_task2 acquired the semaphore %lu\n",
            getsem_task2_got_sem);
    printf("****************************************************************\n");
    printf("\n");
  }
}

void getsem_task1(void* pdata) {
  INT8U return_code = OS_NO_ERR;
  
  while (1) {
    OSSemPend(shared_resource_sem, 0, &return_code);
    alt_ucosii_check_return_code(return_code);
    strcpy(&sem_owner_task_name[0], "getsem_task1");
    getsem_task1_got_sem++;
    OSSemPost(shared_resource_sem);
    OSTimeDlyHMSM(0, 0, 0, 100);
  }
}

void getsem_task2(void* pdata) {
  INT8U return_code = OS_NO_ERR;
  INT8U led;
  led = 1;
  while (1) {
    led = led << 1;
	if (led == 0) led = 1;
	IOWR_ALTERA_AVALON_PIO_DATA(GREEN_LED_BASE,led);

	OSSemPend(shared_resource_sem, 0, &return_code);
    strcpy(&sem_owner_task_name[0], "getsem_task2");
    alt_ucosii_check_return_code(return_code);
    getsem_task2_got_sem++;
    OSSemPost(shared_resource_sem);
    OSTimeDlyHMSM(0, 0, 0, 130);
  }
}

void send_task(void* pdata) {
  INT8U return_code = OS_ERR_NONE;
  INT32U msg;
  
  while (1) {
      msg = (INT32U) OSQPend(buttonqueue, 0, &return_code);
      if (return_code != OS_ERR_NONE) continue;
      return_code = OSQPostOpt(msgqueue, (void*) msg, OS_POST_OPT_BROADCAST);
      alt_ucosii_check_return_code(return_code);
  }
}

void receive_task1(void* pdata) {
  INT8U return_code = OS_ERR_NONE;
  INT32U msg;
  
  while (1) {
    msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
    alt_ucosii_check_return_code(return_code);
    sevenseg_set_hex(msg);
  }
}

void receive_task2(void* pdata) {
  INT8U return_code = OS_NO_ERR;
  INT32U msg;
  
  while (1) {
    msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
    alt_ucosii_check_return_code(return_code);
    printf("pressed %d", msg);
    if (btnLogPtr < 31)
    	btnLog[btnLogPtr++] = msg;
  }
}

void isr_button(void* context, alt_u32 id) {
  INT32U value;
  INT8U return_code = OS_ERR_NONE;
  //inform entering isr
  OSIntEnter();
  value = IORD_ALTERA_AVALON_PIO_DATA(BUTTONS_BASE);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE,0);
  //send to queue
  return_code = OSQPostOpt(buttonqueue, (void *)value, OS_POST_OPT_BROADCAST);
  alt_ucosii_check_return_code(return_code);
  IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE);
  //inform leaving isr
  OSIntExit();
}

void initialize_task(void* pdata) {
  INT8U return_code = OS_NO_ERR;
    
  initOSDataStructs();  

  initCreateTasks();
  
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTONS_BASE, 0xf);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE, 0x0);
  return_code = alt_irq_register(BUTTONS_IRQ, NULL, isr_button);

  return_code = OSTaskDel(OS_PRIO_SELF);
  alt_ucosii_check_return_code(return_code);


  while (1);
}

int main (int argc, char* argv[], char* envp[]) {
  INT8U return_code = OS_NO_ERR;
  return_code = OSTaskCreate(initialize_task, NULL,
                             (void *)&initialize_task_stk[TASK_STACKSIZE-1],
                             INITIALIZE_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  OSStart();
  return 0;
}

int initOSDataStructs(void) {
  msgqueue = OSQCreate(&msgqueueTbl[0], MSG_QUEUE_SIZE);
  buttonqueue = OSQCreate(&buttonqueueTbl[0], MSG_QUEUE_SIZE);
  shared_resource_sem = OSSemCreate(1);
  return 0;
}
 
int initCreateTasks(void) {
  INT8U return_code = OS_NO_ERR;

  return_code = OSTaskCreate(getsem_task1, NULL,
                             (void *)&getsem_task1_stk[TASK_STACKSIZE-1],
                             GETSEM_TASK1_PRIORITY);
  return_code = OSTaskCreate(getsem_task2, NULL,
                             (void *)&getsem_task2_stk[TASK_STACKSIZE-1],
                             GETSEM_TASK2_PRIORITY);
  return_code = OSTaskCreate(receive_task1, NULL,
                             (void *)&receive_task1_stk[TASK_STACKSIZE-1],
                             RECEIVE_TASK1_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(receive_task2, NULL,
                             (void *)&receive_task2_stk[TASK_STACKSIZE-1],
                             RECEIVE_TASK2_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(send_task, NULL,
                             (void *)&send_task_stk[TASK_STACKSIZE-1],
                             SEND_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(print_status_task, NULL,
                             (void *)&print_status_task_stk[TASK_STACKSIZE-1],
                             PRINT_STATUS_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return 0;
}
\end{lstlisting}

\subsection{Полный листинг программы выполняющей индивидуальное задание}
\label{appendix:indi}
\begin{lstlisting}[language=C,style=CCodeStyle]
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "includes.h"
#include "alt_ucosii_simple_error_check.h"
#include "altera_avalon_pio_regs.h"

/* Ñòåêè çàäà÷ */
#define   TASK_STACKSIZE       1024
OS_STK    initialize_task_stk[TASK_STACKSIZE];
OS_STK    print_status_task_stk[TASK_STACKSIZE];
OS_STK    task_btn1_stk[TASK_STACKSIZE];
OS_STK    task_btn2_stk[TASK_STACKSIZE];
OS_STK    task_btn3_stk[TASK_STACKSIZE];
OS_STK    task_btn4_stk[TASK_STACKSIZE];
OS_STK    task_sseg_stk[TASK_STACKSIZE];
OS_STK    task_manage_stk[TASK_STACKSIZE];

/* Ïðèîðèòåòû çàäà÷ */
#define INITIALIZE_TASK_PRIORITY   6
#define PRINT_STATUS_TASK_PRIORITY 7
#define task_sseg_PRIORITY    9
#define task_btn1_PRIORITY    10
#define task_btn2_PRIORITY    11
#define task_btn3_PRIORITY    12
#define task_btn4_PRIORITY    13
#define task_manage_PRIORITY  14

#define SEV_SEG_SWITCH 2
#define SEV_SEG_RESET  3
#define BTN_1_IRQ      7
#define BTN_2_IRQ      11
#define BTN_3_IRQ      13
#define BTN_4_IRQ      14
#define BTN_1_SHIFT    4096
#define BTN_2_SHIFT    256
#define BTN_3_SHIFT    16
#define BTN_4_SHIFT    1

/* Îáúÿâëåíèå î÷åðåäè */
#define   MSG_QUEUE_SIZE  30            //Size of message queue used in example
OS_EVENT  *msgqueue;                    //Message queue pointer 
void      *msgqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages
OS_EVENT  *buttonqueue;                    //Message queue pointer
void      *buttonqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages
OS_EVENT  *ssegqueue;                    //Message queue pointer
void      *ssegqueueTbl[MSG_QUEUE_SIZE]; // Storage for messages
int        btnLog[32];
int        btnLogPtr = 0;

/* Ãëîáàëüíûå ïåðåìåííûå */
INT32U number_of_messages_sent = 0;
INT32U number_of_messages_received_task1 = 0;
INT32U number_of_messages_received_task2 = 0;

volatile int isOn = 0;
OS_TMR *Timer2Hz;

/* Ïðîòîòèïû ôóíêöèé */
int initOSDataStructs(void);
int initCreateTasks(void);

static void sevenseg_set_hex(int hex) {
  static alt_u8 segments[16] = {
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, /* 0-9 */
    0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E };                       /* a-f */

  unsigned int data = segments[hex & 15] | (segments[(hex >> 4) & 15] << 8) |
		  (segments[(hex >> 8) & 15] << 16) | (segments[(hex >> 12) & 15] << 24);

  IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_BASE, data);
}

void print_status_task(void* pdata) {
  INT8U retCode = OS_ERR_NONE;
  INT8U bretCode = OS_ERR_NONE;
  OS_Q_DATA qData;
  OS_Q_DATA qbData;

  while (1) {
    OSTimeDlyHMSM(0, 0, 10, 0);
    printf("TIME: %10lu sec\n", OSTimeGet() / OS_TICKS_PER_SEC);
    retCode = OSQQuery(msgqueue, &qData);
    if (retCode == OS_ERR_NONE) {
      printf("MESSAGES: %7i\n", qData.OSNMsgs);
    } else {
      printf("QUEUE_ERR: %6i\n", retCode);
    }
    int p;
    for (p = 0; p < 32; ++p)
    	printf("%d, ", btnLog[p]);
    printf("\n");
    for (p = 0; p < 32; ++p)
    	btnLog[p] = 0;
    btnLogPtr = 0;

    bretCode = OSQQuery(buttonqueue, &qbData);
    if (bretCode == OS_ERR_NONE) {
      printf("BTNS MSGS: %7i\n", qbData.OSNMsgs);
    } else {
      printf("QUEUE_ERR: %6i\n", bretCode);
    }
    printf("****************************************************************\n");
    printf("Hello From MicroC/OS-II Running on NIOS II.  Here is the status:\n");
    printf("The number of messages sent by the task_manage:         %lu\n",
            number_of_messages_sent);
    printf("The number of messages received by the task_btn1: %lu\n",
            number_of_messages_received_task1);
    printf("The number of messages received by the task_btn2: %lu\n",
            number_of_messages_received_task2);
    printf("****************************************************************\n");
    printf("\n");
  }
}

void task_sseg(void *pdata) {
    INT8U return_code = OS_ERR_NONE;
	INT32U msg;
	INT32U buttons;
	INT32U value = 0;
	INT32U multiplier;

	while (1) {
		//pend q
	    msg = (INT32U)OSQPend(ssegqueue, 0, &return_code);
		alt_ucosii_check_return_code(return_code);
		// is something pressed?
		buttons = IORD_ALTERA_AVALON_PIO_DATA(BUTTONS_BASE);
		// if only one is pressed
		if (buttons != 15) {
			// if msg from timer = switch visiblity
			switch (msg) {
			case SEV_SEG_SWITCH:
				isOn = ~isOn;
				break;
			case SEV_SEG_RESET:
				value = 0;
				break;
			case BTN_1_SHIFT:
			case BTN_2_SHIFT:
			case BTN_3_SHIFT:
			case BTN_4_SHIFT:
				multiplier = msg;
				break;
			default:
				printf("something went very wrong!");
			}

			if (isOn) {
				IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_BASE, -1);
				value++;
			} else {
				sevenseg_set_hex((value & 15) * multiplier);
			}
		}
	}
}
void task_manage(void* pdata) {
  INT8U return_code = OS_ERR_NONE;
  INT32U msg;
  
  while (1) {
	  //when a message from ISR is pending
      msg = (INT32U) OSQPend(buttonqueue, 0, &return_code);
      if (return_code != OS_ERR_NONE) continue;
      // filter if more than one button is pressed and send to q
      if (msg == BTN_1_IRQ || msg == BTN_2_IRQ || msg == BTN_3_IRQ || msg == BTN_4_IRQ) {
          return_code = OSQPostOpt(msgqueue, (void*) msg, OS_POST_OPT_BROADCAST);
          alt_ucosii_check_return_code(return_code);
          return_code = OSQPostOpt(ssegqueue, (void*) SEV_SEG_RESET, OS_POST_OPT_BROADCAST);
      	  alt_ucosii_check_return_code(return_code);
      }
  }
}
void task_btn1(void* pdata) {
    INT8U return_code = OS_ERR_NONE;
    INT32U msg = 0;
  
    while (1) {
	    // read from q
        msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
        alt_ucosii_check_return_code(return_code);
        // if it's my btn - send to sseg task a shift for counter
        if (msg == BTN_1_IRQ) {
        	return_code = OSQPostOpt(ssegqueue, (void*) BTN_1_SHIFT, OS_POST_OPT_BROADCAST);
        	alt_ucosii_check_return_code(return_code);
        }
    }
}
void task_btn2(void* pdata) {
	INT8U return_code = OS_ERR_NONE;
	INT32U msg = 0;

	while (1) {
		// read from q
	    msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
	    alt_ucosii_check_return_code(return_code);
	    // if it's my btn - send to sseg task a shift for counter
	    if (msg == BTN_2_IRQ) {
	    	return_code = OSQPostOpt(ssegqueue, (void*) BTN_2_SHIFT, OS_POST_OPT_BROADCAST);
	    	alt_ucosii_check_return_code(return_code);
	    }
	  }
}
void task_btn3(void* pdata) {
	INT8U return_code = OS_ERR_NONE;
	INT32U msg = 0;

	while (1) {
		// read from q
	    msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
	    alt_ucosii_check_return_code(return_code);
	    // if it's my btn - send to sseg task a shift for counter
	    if (msg == BTN_3_IRQ) {
	    	return_code = OSQPostOpt(ssegqueue, (void*) BTN_3_SHIFT, OS_POST_OPT_BROADCAST);
	    	alt_ucosii_check_return_code(return_code);
	    }
	  }
}
void task_btn4(void* pdata) {
	INT8U return_code = OS_ERR_NONE;
	INT32U msg = 0;

	while (1) {
		// read from q
	    msg = (INT32U)OSQPend(msgqueue, 0, &return_code);
	    alt_ucosii_check_return_code(return_code);
	    // if it's my btn - send to sseg task a shift for counter
	    if (msg == BTN_4_IRQ) {
	    	return_code = OSQPostOpt(ssegqueue, (void*) BTN_4_SHIFT, OS_POST_OPT_BROADCAST);
	    	alt_ucosii_check_return_code(return_code);
	    }
	  }
}
void timer2Hz(void* ptmr, void* args) {
	INT8U return_code = OS_ERR_NONE;
	return_code = OSQPostOpt(ssegqueue, (void *)SEV_SEG_SWITCH, OS_POST_OPT_BROADCAST);
	alt_ucosii_check_return_code(return_code);
}
void isr_button(void* context, alt_u32 id) {
  INT32U value;
  INT8U return_code = OS_ERR_NONE;
  //inform entering isr
  OSIntEnter();
  // read PIO
  value = IORD_ALTERA_AVALON_PIO_DATA(BUTTONS_BASE);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE,0);
  //send to queue
  return_code = OSQPostOpt(buttonqueue, (void *)value, OS_POST_OPT_BROADCAST);
  alt_ucosii_check_return_code(return_code);
  IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE);
  //inform leaving isr
  OSIntExit();
}

void initialize_task(void* pdata) {
  INT8U return_code = OS_ERR_NONE;

  Timer2Hz = OSTmrCreate(0, 5, OS_TMR_OPT_PERIODIC, timer2Hz, (void*) 0, "blink 2hz", &return_code);
  alt_ucosii_check_return_code(return_code);
  OSTmrStart(Timer2Hz, &return_code);
  alt_ucosii_check_return_code(return_code);

  initOSDataStructs();  
  initCreateTasks();
  
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTONS_BASE, 0xf);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE, 0x0);
  IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_BASE, -1);
  return_code = alt_irq_register(BUTTONS_IRQ, NULL, isr_button);

  return_code = OSTaskDel(OS_PRIO_SELF);
  alt_ucosii_check_return_code(return_code);


  while (1);
}

int main (int argc, char* argv[], char* envp[])
{
  INT8U return_code = OS_ERR_NONE;
  return_code = OSTaskCreate(initialize_task,
                             NULL,
                             (void *)&initialize_task_stk[TASK_STACKSIZE-1],
                             INITIALIZE_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  OSStart();
  return 0;
}

int initOSDataStructs(void)
{
  msgqueue = OSQCreate(&msgqueueTbl[0], MSG_QUEUE_SIZE);
  buttonqueue = OSQCreate(&buttonqueueTbl[0], MSG_QUEUE_SIZE);
  ssegqueue = OSQCreate(&ssegqueueTbl[0], MSG_QUEUE_SIZE);
  return 0;
}
 
int initCreateTasks(void)
{
  INT8U return_code = OS_ERR_NONE;

  return_code = OSTaskCreate(task_sseg,
                             NULL,
                             (void *)&task_sseg_stk[TASK_STACKSIZE-1],
                             task_sseg_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(task_btn1,
                             NULL,
                             (void *)&task_btn1_stk[TASK_STACKSIZE-1],
                             task_btn1_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(task_btn2,
                             NULL,
                             (void *)&task_btn2_stk[TASK_STACKSIZE-1],
                             task_btn2_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(task_btn3,
                             NULL,
                             (void *)&task_btn3_stk[TASK_STACKSIZE-1],
                             task_btn3_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(task_btn4,
                             NULL,
                             (void *)&task_btn4_stk[TASK_STACKSIZE-1],
                             task_btn4_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(task_manage,
                             NULL,
                             (void *)&task_manage_stk[TASK_STACKSIZE-1],
                             task_manage_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return_code = OSTaskCreate(print_status_task,
                             NULL,
                             (void *)&print_status_task_stk[TASK_STACKSIZE-1],
                             PRINT_STATUS_TASK_PRIORITY);
  alt_ucosii_check_return_code(return_code);
  return 0;
}
\end{lstlisting}


\end{document}

