Загрузка данных


#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <stdint.h>

/* =========================================================
   НАСТРОЙКИ
   ========================================================= */

#define N_TAKTS             14

/* Сенсоры */
#define SENS_OBUCH          9
#define SENS_KONTROL        10
#define SENS_YES            11
#define SENS_NO              12
#define SENS_PAUSE          13

/* Светодиоды */
#define LED_OBUCH           9
#define LED_KONTROL         10
#define LED_YES             11
#define LED_NO              12
#define LED_PAUSE           13

/* Количество вопросов */
#define NUM_QST             10


/* =========================================================
   СДВИГОВЫЕ РЕГИСТРЫ / СЕНСОРНАЯ ЛЕНТА

   Было в исходном UniShift:
       DATA = PB5
       CLK  = PB7

       SHCP = PD6
       STCP = PD7
       DATA INPUT = PD5

   Светодиоды хранятся в Out[].
   ========================================================= */

#define OUT_DATA_PORT       PORTB
#define OUT_DATA_DDR        DDRB
#define OUT_DATA_PIN        PB5

#define OUT_CLK_PORT        PORTB
#define OUT_CLK_DDR         DDRB
#define OUT_CLK_PIN         PB7

#define SHIFT_CLK_PORT      PORTD
#define SHIFT_CLK_DDR       DDRD
#define SHIFT_CLK_PIN       PD6

#define SHIFT_LATCH_PORT    PORTD
#define SHIFT_LATCH_DDR     DDRD
#define SHIFT_LATCH_PIN     PD7

#define SENSOR_DATA_PORT    PIND
#define SENSOR_DATA_DDR     DDRD
#define SENSOR_DATA_PIN     PD5


#define DS                  (1 << PD3)

/*
 * В оригинальном UniShift было:
 *
 * N_takts_Out = 32
 * Out[0..32]
 *
 * Оставляем этот же размер.
 */
#define N_TAKTS_OUT         32


/* =========================================================
   ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
   ========================================================= */

volatile uint8_t num = 0;
volatile uint8_t num_old = 0;

volatile uint8_t play_active = 0;
/*
   0 = ничего не воспроизводится
   1 = воспроизводится
   2 = плеер сообщил об окончании
*/

volatile uint8_t pause_state = 0;
/*
   0 = воспроизведение
   1 = пауза
*/

volatile uint8_t ik_new = 0;

volatile uint8_t punkt = 1;
volatile uint8_t razd = 1;

volatile uint8_t rndcase = 1;
volatile uint8_t was[NUM_QST + 1];
volatile uint8_t rnd_max = NUM_QST;

volatile uint8_t out = 0;
volatile uint8_t all = 0;
volatile uint8_t k = 0;

volatile uint8_t answ[NUM_QST + 1] =
{
    0,
    11,
    12,
    11,
    12,
    11,
    12,
    11,
    12,
    11,
    11
};


/* Буфер светодиодов */
volatile uint8_t Out[N_TAKTS_OUT + 1];


/* Счётчик сдвига */
volatile uint8_t takt_cnt = 0;


/* =========================================================
   UART
   ========================================================= */

static void UART_SendByte(uint8_t data)
{
    while (!(UCSRA & (1 << UDRE)))
    {
        /* ждём освобождения регистра */
    }

    UDR = data;
}


/*
 * Плеер использует ASCII_PROTOCOL.
 *
 * Для PLAY(201) отправляем:
 *
 * FF 32 30 31
 *
 * где:
 * FF = начало пакета
 * 32 = '2'
 * 30 = '0'
 * 31 = '1'
 */
static void PLAYER_SendTrack(uint16_t track)
{
    uint8_t a;
    uint8_t b;
    uint8_t c;

    if (track > 999)
        track = 999;

    a = track / 100;
    b = (track / 10) % 10;
    c = track % 10;

    /*
     * Сначала полностью дождаться предыдущего байта.
     */
    UART_SendByte(0xFF);
    UART_SendByte('0' + a);
    UART_SendByte('0' + b);
    UART_SendByte('0' + c);
}


/* Команды плееру */
static void PLAYER_Pause(void)
{
    UART_SendByte('p');
}

static void PLAYER_Resume(void)
{
    UART_SendByte('r');
}

static void PLAYER_Stop(void)
{
    UART_SendByte('s');
}


/*
 * Основная функция запуска трека.
 */
static void PLAY(uint16_t track)
{
    /*
     * Сбрасываем состояние паузы перед новым треком.
     */
    pause_state = 0;

    PLAYER_SendTrack(track);

    /*
     * После отправки пакета считаем, что трек запущен.
     */
    play_active = 1;
}


/* =========================================================
   UART RX
   ========================================================= */

/*
 * Плеер при окончании трека присылает '@'.
 */
ISR(USARTRXC_vect)
{
    uint8_t status;
    uint8_t data;

    status = UCSRA;
    data = UDR;

    (void)status;

    if (data == '@')
    {
        play_active = 2;
    }
}


/* =========================================================
   СДВИГОВЫЙ РЕГИСТР
   ========================================================= */

/*
 * Функция полностью заменяет Shift_Takt()
 * из UniShift_Out_old.c.
 */
static void Shift_Takt(void)
{
    uint8_t index;

    /*
     * Начало передачи.
     */
    SHIFT_CLK_PORT &= ~(1 << SHIFT_CLK_PIN);
    SHIFT_LATCH_PORT &= ~(1 << SHIFT_LATCH_PIN);

    /*
     * Выводим 32 бита.
     *
     * Сохраняем направление из оригинального UniShift.
     */
    for (index = 0; index < N_TAKTS_OUT; index++)
    {
        /*
         * DATA
         */
        if (Out[index + 1])
            OUT_DATA_PORT |= (1 << OUT_DATA_PIN);
        else
            OUT_DATA_PORT &= ~(1 << OUT_DATA_PIN);

        /*
         * CLK HIGH
         */
        OUT_CLK_PORT |= (1 << OUT_CLK_PIN);

        /*
         * CLK LOW
         */
        OUT_CLK_PORT &= ~(1 << OUT_CLK_PIN);
    }

    /*
     * Защёлка.
     */
    SHIFT_LATCH_PORT |= (1 << SHIFT_LATCH_PIN);

    SHIFT_LATCH_PORT &= ~(1 << SHIFT_LATCH_PIN);
}


/* =========================================================
   TIMER2
   ========================================================= */

ISR(TIMER2_OVF_vect)
{
    TCNT2 = 2;

    Shift_Takt();
}


/* =========================================================
   ОПРОС СЕНСОРНОЙ ЛЕНТЫ
   ========================================================= */

static void Opros(void)
{
    uint8_t j;

    num = 0;

    /*
     * LATCH LOW
     */
    SHIFT_LATCH_PORT &= ~(1 << SHIFT_LATCH_PIN);

    _delay_us(1);

    /*
     * LATCH HIGH
     */
    SHIFT_LATCH_PORT |= (1 << SHIFT_LATCH_PIN);

    _delay_us(1);

    /*
     * Читаем 14 сенсоров.
     */
    for (j = 1; j <= N_TAKTS; j++)
    {
        /*
         * Сенсор активен низким уровнем.
         */
        if (!(SENSOR_DATA_PORT & (1 << SENSOR_DATA_PIN)))
        {
            num = j;
        }

        /*
         * Следующий сенсор.
         */
        SHIFT_CLK_PORT &= ~(1 << SHIFT_CLK_PIN);
        _delay_us(1);

        SHIFT_CLK_PORT |= (1 << SHIFT_CLK_PIN);
        _delay_us(1);
    }

    _delay_ms(1);
}


/* =========================================================
   ВСЕ СВЕТОДИОДЫ ВЫКЛ
   ========================================================= */

static void ALL_OFF(void)
{
    uint8_t j;

    /*
     * В исходном коде светодиоды 1..8
     * выключались здесь.
     */
    for (j = 1; j < LED_OBUCH; j++)
    {
        Out[16 + j] &= ~DS;
    }
}


/* =========================================================
   ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ LED
   ========================================================= */

static void LED_On(uint8_t led)
{
    if (led <= N_TAKTS_OUT)
    {
        Out[16 + led] |= DS;
    }
}


static void LED_Off(uint8_t led)
{
    if (led <= N_TAKTS_OUT)
    {
        Out[16 + led] &= ~DS;
    }
}


/* =========================================================
   ПАУЗА / ПРОДОЛЖЕНИЕ
   ========================================================= */

static void HandlePause(void)
{
    /*
     * ВАЖНО:
     * пауза разрешена только когда действительно
     * есть активный трек.
     */
    if (play_active != 1)
        return;

    if (num != SENS_PAUSE)
        return;

    /*
     * Первый клик = PAUSE
     * Второй клик = RESUME
     */
    if (pause_state == 0)
    {
        PLAYER_Pause();

        pause_state = 1;

        LED_On(LED_PAUSE);
    }
    else
    {
        PLAYER_Resume();

        pause_state = 0;

        LED_Off(LED_PAUSE);
    }

    /*
     * Ждём отпускания сенсора.
     * Это важно: без этого одно физическое нажатие
     * может быть обработано несколько раз.
     */
    do
    {
        Opros();
    }
    while (num == SENS_PAUSE);

    LED_Off(LED_PAUSE);

    _delay_ms(50);
}


/* =========================================================
   ОБРАБОТКА ОКОНЧАНИЯ ТРЕКА
   ========================================================= */

static void HandleTrackFinished(void)
{
    if (play_active != 2)
        return;

    play_active = 0;
    pause_state = 0;

    if (num_old != 0)
    {
        LED_Off(num_old);
        num_old = 0;
    }

    LED_Off(LED_PAUSE);
}


/* =========================================================
   ВЫБОР ТРЕКА НА СЕНСОРЕ
   ========================================================= */

static void HandleTrackSensor(void)
{
    /*
     * Сенсоры 1..8 = выбор элементов.
     */
    if (num == 0 || num >= SENS_OBUCH)
        return;

    /*
     * Повторное нажатие того же сенсора:
     * остановить трек.
     */
    if (num_old == num)
    {
        PLAYER_Stop();

        play_active = 0;
        pause_state = 0;

        LED_Off(num_old);
        LED_Off(LED_PAUSE);

        num_old = 0;
    }
    else
    {
        /*
         * Запускаем выбранный трек.
         */
        PLAY(num);

        /*
         * Выключаем старый LED.
         */
        if (num_old != 0)
            LED_Off(num_old);

        /*
         * Включаем новый LED.
         */
        LED_On(num);

        num_old = num;
    }

    /*
     * Ждём отпускания сенсора.
     */
    do
    {
        Opros();
    }
    while (num != 0);

    _delay_ms(50);
}


/* =========================================================
   РЕЖИМ ОБУЧЕНИЯ
   ========================================================= */

static void STUDY(void)
{
    /*
     * Включаем LED "Обучение".
     */
    LED_On(LED_OBUCH);

    /*
     * Основной трек обучения.
     *
     * В вашем исходном коде это 201.
     */
    PLAY(201);

    /*
     * Больше НЕ делаем:
     *
     *     _delay_ms(1000);
     *
     * потому что во время этого delay AVR не опрашивает
     * сенсоры.
     */

    razd = 1;
    punkt = 1;

    /*
     * Ждём перехода в "Контроль".
     */
    while (num != SENS_KONTROL)
    {
        Opros();

        /*
         * Пауза.
         */
        HandlePause();

        /*
         * Окончание трека.
         */
        HandleTrackFinished();

        /*
         * Выбор сенсора 1..8.
         */
        HandleTrackSensor();

        /*
         * Если нажали "Контроль",
         * выходим.
         */
        if (num == SENS_KONTROL)
            break;

        _delay_ms(5);
    }

    /*
     * Остановка плеера.
     */
    PLAYER_Stop();

    play_active = 0;
    pause_state = 0;
    num_old = 0;

    ALL_OFF();

    LED_Off(LED_OBUCH);
    LED_Off(LED_PAUSE);

    /*
     * Ждём отпускания "Контроль".
     */
    do
    {
        Opros();
    }
    while (num == SENS_KONTROL);

    _delay_ms(100);
}


/* =========================================================
   СЛУЧАЙНЫЙ ВОПРОС
   ========================================================= */

static void RND(void)
{
    uint8_t attempts;

    attempts = 0;

    do
    {
        rndcase = 1 + (rand() % rnd_max);

        attempts++;

        /*
         * Защита от бесконечного цикла.
         */
        if (attempts > 50)
        {
            uint8_t i;

            for (i = 0; i <= NUM_QST; i++)
                was[i] = 0;

            attempts = 0;
        }

    }
    while (was[rndcase] != 0);

    was[rndcase] = 1;

    /*
     * Когда почти все вопросы использованы,
     * очищаем историю.
     */
    all = 0;

    {
        uint8_t i;

        for (i = 1; i <= rnd_max; i++)
        {
            if (was[i])
                all++;
        }

        if (all >= rnd_max - 2)
        {
            for (i = 0; i <= NUM_QST; i++)
                was[i] = 0;

            /*
             * Текущий вопрос считаем использованным.
             */
            was[rndcase] = 1;
        }
    }
}


/* =========================================================
   РЕЖИМ КОНТРОЛЯ
   ========================================================= */

static void KONTROL(void)
{
    LED_On(LED_KONTROL);

    /*
     * Вступительный трек контроля.
     */
    PLAY(202);

    /*
     * В оригинале был delay 1000 мс.
     * Оставляем небольшую задержку, но не блокируем
     * управление на целую секунду.
     */
    _delay_ms(100);

    srand(TCNT2);

    out = 1;

    razd = 1;
    punkt = 1;

    while (num != SENS_OBUCH)
    {
        Opros();

        /*
         * Выбор нового вопроса.
         */
        if (out == 1)
        {
            RND();

            _delay_ms(50);

            /*
             * В исходном коде:
             * PLAY(rndcase + 100)
             */
            PLAY(rndcase + 100);

            out = 0;
        }

        /*
         * Пауза.
         */
        HandlePause();

        /*
         * Окончание воспроизведения.
         */
        if (play_active == 2)
        {
            play_active = 0;
            pause_state = 0;
            LED_Off(LED_PAUSE);
        }

        /*
         * Ответ "ДА".
         */
        if (num == SENS_YES)
        {
            if (num == answ[rndcase])
            {
                LED_On(LED_YES);

                PLAY(203);

                _delay_ms(100);

                LED_Off(LED_YES);

                out = 1;
            }
            else
            {
                LED_On(LED_NO);

                PLAY(204);

                _delay_ms(100);

                LED_Off(LED_NO);

                out = 1;
            }

            /*
             * Ждём отпускания сенсора.
             */
            do
            {
                Opros();
            }
            while (num == SENS_YES);
        }


        /*
         * Ответ "НЕТ".
         */
        if (num == SENS_NO)
        {
            if (num == answ[rndcase])
            {
                LED_On(LED_YES);

                PLAY(203);

                _delay_ms(100);

                LED_Off(LED_YES);

                out = 1;
            }
            else
            {
                LED_On(LED_NO);

                PLAY(204);

                _delay_ms(100);

                LED_Off(LED_NO);

                out = 1;
            }

            /*
             * Ждём отпускания.
             */
            do
            {
                Opros();
            }
            while (num == SENS_NO);
        }

        /*
         * Выход в обучение.
         */
        if (num == SENS_OBUCH)
            break;

        _delay_ms(5);
    }

    /*
     * Стоп.
     */
    PLAYER_Stop();

    play_active = 0;
    pause_state = 0;

    ALL_OFF();

    LED_Off(LED_KONTROL);
    LED_Off(LED_PAUSE);

    /*
     * Ждём отпускания "Обучение".
     */
    do
    {
        Opros();
    }
    while (num == SENS_OBUCH);

    _delay_ms(100);
}


/* =========================================================
   DEMO
   ========================================================= */

static void DEMO(void)
{
    ALL_OFF();

    punkt = 1;

    PLAY(punkt);

    LED_On(punkt);

    _delay_ms(100);

    /*
     * Ждём отпускания.
     */
    do
    {
        Opros();
    }
    while (num != 0);

    num = 0;

    while ((num != SENS_OBUCH) &&
           (num != SENS_KONTROL))
    {
        Opros();

        /*
         * Трек закончился.
         */
        if (play_active == 2)
        {
            play_active = 0;

            LED_Off(punkt);
            LED_Off(razd);

            punkt++;

            if (punkt >= 18)
                punkt = 1;

            /*
             * Запускаем следующий.
             */
            PLAY(punkt);

            /*
             * Раздел.
             */
            if ((punkt == 1) ||
                (punkt == 2) ||
                (punkt == 3))
            {
                razd = punkt;
            }

            if ((punkt > 3) &&
                (punkt < 10))
            {
                razd = 3;
            }

            if ((punkt > 10) &&
                (punkt < 14))
            {
                razd = 10;
            }

            if ((punkt > 14) &&
                (punkt < 18))
            {
                razd = 14;
            }

            LED_On(punkt);
            LED_On(razd);
        }

        /*
         * Пауза.
         */
        HandlePause();

        _delay_ms(5);
    }

    PLAYER_Stop();

    play_active = 0;
    pause_state = 0;

    ALL_OFF();

    LED_Off(LED_PAUSE);

    do
    {
        Opros();
    }
    while (num != 0);
}


/* =========================================================
   ИНИЦИАЛИЗАЦИЯ UART
   ========================================================= */

static void UART_Init(void)
{
    /*
     * 8 MHz / 16 / (51 + 1) = ~9615 бод.
     *
     * Это соответствует 9600 бод плеера.
     */
    UBRRH = 0;
    UBRRL = 51;

    /*
     * RX + TX + RX interrupt.
     */
    UCSRB =
        (1 << RXEN) |
        (1 << TXEN) |
        (1 << RXCIE);

    /*
     * 8 бит, 1 стоп, без parity.
     */
    UCSRC =
        (1 << URSEL) |
        (1 << UCSZ1) |
        (1 << UCSZ0);
}


/* =========================================================
   ИНИЦИАЛИЗАЦИЯ СЕНСОРНОЙ ЛЕНТЫ
   ========================================================= */

static void Shift_Init(void)
{
    /*
     * PB5 = DATA OUT
     * PB7 = CLK OUT
     */
    OUT_DATA_DDR |= (1 << OUT_DATA_PIN);
    OUT_CLK_DDR  |= (1 << OUT_CLK_PIN);

    OUT_DATA_PORT &= ~(1 << OUT_DATA_PIN);
    OUT_CLK_PORT  &= ~(1 << OUT_CLK_PIN);


    /*
     * PD6 = SHCP
     * PD7 = STCP
     */
    SHIFT_CLK_DDR   |= (1 << SHIFT_CLK_PIN);
    SHIFT_LATCH_DDR |= (1 << SHIFT_LATCH_PIN);

    SHIFT_CLK_PORT   &= ~(1 << SHIFT_CLK_PIN);
    SHIFT_LATCH_PORT &= ~(1 << SHIFT_LATCH_PIN);


    /*
     * PD5 = DATA INPUT.
     */
    SENSOR_DATA_DDR &= ~(1 << SENSOR_DATA_PIN);

    /*
     * В исходном проекте подтяжка здесь не включалась,
     * поэтому оставляем как было.
     */
    PORTD &= ~(1 << SENSOR_DATA_PIN);
}


/* =========================================================
   ИНИЦИАЛИЗАЦИЯ TIMER2
   ========================================================= */

static void Timer2_Init(void)
{
    /*
     * В исходнике:
     *
     * TCCR2 = TCCR2 | (1<<CS22);
     * TIMSK = TIMSK | (1<<TOIE2);
     * TCNT2 = 1;
     *
     * Оставляем ту же частоту/делитель.
     */
    TCCR2 |= (1 << CS22);

    TCNT2 = 1;

    TIMSK |= (1 << TOIE2);
}


/* =========================================================
   MAIN
   ========================================================= */

int main(void)
{
    uint8_t i;


    /* -----------------------------------------------------
       Инициализация портов
       ----------------------------------------------------- */

    /*
     * Сохраняем направления из исходного проекта:
     *
     * DDRB = 0b10100000
     * DDRC = 0b11000000
     * DDRD = 0b11001000
     */

    DDRB = 0b10100000;
    PORTB = 0b00000000;

    DDRC = 0b11000000;
    PORTC = 0b00111111;

    DDRD = 0b11001000;
    PORTD = 0b00100000;


    /*
     * Обнуляем массив LED.
     */
    for (i = 0; i <= N_TAKTS_OUT; i++)
    {
        Out[i] = 0;
    }


    /*
     * Инициализация.
     */
    Shift_Init();
    Timer2_Init();
    UART_Init();


    /*
     * Начальные значения.
     */
    play_active = 0;
    pause_state = 0;
    num_old = 0;
    num = 0;

    for (i = 0; i <= NUM_QST; i++)
    {
        was[i] = 0;
    }


    /*
     * Разрешаем прерывания.
     */
    sei();


    /*
     * Первый вывод.
     */
    Shift_Takt();

    _delay_ms(300);

    Opros();


    /* -----------------------------------------------------
       ОСНОВНОЙ ЦИКЛ
       ----------------------------------------------------- */

    while (1)
    {
        Opros();


        /*
         * ОБУЧЕНИЕ
         */
        if (num == SENS_OBUCH)
        {
            /*
             * Ждём отпускания кнопки, чтобы один клик
             * не запускал режим неск