Загрузка данных
#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)
{
/*
* Ждём отпускания кнопки, чтобы один клик
* не запускал режим неск