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




Вставленный текст.txt
Документ
Привет у меня есть такой проект это стенд на котором 13 сенсоров представляющих из себя кнопки и светодиоды рядом с сенсорами и пару отдельных так вот сейчас такая проблема при нажатии на обучение должна проиграться озвучка а ее нет сразу скажу функции отправляющие данные на плеер считаем верной т.к в целом плеер работает если нажать обучение потом дважды пауза озвучка включится но криво найди ошибки пожалуйста 

Да. Теперь по симптомам я бы перестал копать play_active и паузу как основную причину.

То, что ты описываешь:

нажимаю «Обучение» → нажимаю любой сенсор → тишина → нажимаю паузу → ещё раз паузу → озвучка начинает играть

очень характерно для того, что команда запуска трека до плеера доходит не в том состоянии/формате, а r от второй паузы фактически запускает уже ожидающий трек.

Я посмотрел именно тот stand_fixed.c, который ты залил. В нём есть важный момент: PLAY() отправляет FF + 3 цифры, но после этого мы сами выставляем play_active = 1. 


При этом обработка паузы отправляет:

'p'
при первом нажатии и:

'r'
при втором.

То есть твой симптом буквально может означать:

PLAY(1)
   ↓
плеер получил/подготовил трек
   ↓
не начал воспроизведение
   ↓
первая пауза → p
   ↓
вторая пауза → r
   ↓
плеер наконец начинает воспроизведение
Поэтому сейчас мне нужен один очень важный кусок
Пришли мне именно код плеера, который принимает FF + цифры, p, r, s и отправляет @.

Вот его особенно важно увидеть. Стенд мы уже достаточно разобрали, а теперь проблема, скорее всего, на границе:

стенд → UART → контроллер плеера.

Если код плеера у тебя отдельным .c/.cpp, .h или проектом — загрузи его целиком.

И ещё
Пока не надо снова прошивать очередную версию моего кода. Сначала посмотрим приёмную сторону.

Особенно хочу найти в плеере:

обработчик 0xFF;

обработку трёх цифр;

обработку 'p';

обработку 'r';

обработку 's';

место, где реально запускается play;

место, где отправляется @.

Потому что если окажется, что r у плеера не «resume», а, например, повторно запускает подготовленный трек, мы сразу получим объяснение твоего симптома.

И тогда я дам тебе уже одну финальную версию обоих кодов — стенд + плеер, согласованных между собой.




Реклама
Реклама не влияет на ответы, которые вы получаете от ChatGPT. Ваши чаты остаются конфиденциальными. 
Подробнее о рекламе


Вставленный текст(1).txt
Документ
Вот код плеера 

Ага. Теперь причина стала намного яснее. Я посмотрел код плеера, и нашёл очень важную вещь, которую в прошлый раз мы упустили.

Проблема, судя по коду, находится не в STUDY() как таковом.

Что происходит на самом деле
Плеер получает от стенда последовательность:

FF 2 0 1
для PLAY(201).

Но плеер не запускает трек непосредственно в обработчике UART. Он только разбирает пакет, записывает номер трека и выставляет:

ch_tr = 1;
А реальное:

DFPlayer_Play_track_UserFolder(...)
выполняется уже в основном FreeRTOS-потоке. Это видно в коде плеера: после получения трёх байтов он устанавливает ch_tr = 1, а затем отдельная задача запускает воспроизведение. 


И вот тут возникает гонка команд.

Самое интересное — r
Плеер обрабатывает r так:

if (MainBoard_Rx[0] == 'r')
{
    DFPlayer_Play();
    DFPlayer1.DF_LED = 0x01;
    LED_BUSY(1);
}
То есть r действительно запускает воспроизведение текущего трека. 


Поэтому твой симптом:

обучение → тишина → два раза пауза → появляется звук

очень похож на следующее:

PLAY(201)
   ↓
плеер получает номер 201
   ↓
записывает его как текущий трек
   ↓
ch_tr = 1
   ↓
...
r
   ↓
DFPlayer_Play()
   ↓
звук появляется
То есть вторая пауза фактически запускает уже выбранный трек.

И вот где я вижу главную ошибку стенда
В твоём AVR-коде PLAY() отправляет четыре байта практически подряд:

UDR = 0xff;
UDR = a + 48;
UDR = b + 48;
UDR = c + 48;
А мы раньше считали, что раз PLAY() в других режимах работает, значит с передачей всё хорошо.

Я теперь не хочу это предполагать.

Плеер ожидает строго:

FF
<3 байта номера>
и только после получения третьего байта устанавливает ch_tr = 1. 


Поэтому я предлагаю сейчас сделать правильную вещь:

Не переписывать ещё раз только STUDY().
Нужно переделать весь обмен AVR ↔ STM32, чтобы:

каждый байт UART реально дождался окончания передачи;

после FF + XXX стенд немного ждал;

плеер успевал обработать ch_tr;

p/r не смешивались с командой выбора трека;

@ использовался только как уведомление об окончании;

PLAY() больше не мог терять байты;

после PLAY(201) звук должен стартовать без единого нажатия паузы.

Причём я бы сделал ещё надёжнее
На стороне AVR сделать:

UART_SendByte(0xFF);
UART_SendByte(...);
UART_SendByte(...);
UART_SendByte(...);
где каждый UART_SendByte() ждёт:

while (!(UCSRA & (1 << UDRE)));
И не использовать напрямую UDR = ... вообще для команд плееру.

На стороне STM32 оставить существующий протокол FF + 3 цифры, p, r, s, поскольку он у тебя уже реализован. Плеер также действительно отправляет @, когда получает событие окончания трека. 


Я теперь могу сделать тебе нормальную финальную пару файлов:

stand.c — полностью исправленный AVR;

плеер — исправленный STM32 код;

единый надёжный протокол обмена;

обучение запускается сразу;

любой сенсор сразу запускает соответствующую озвучку;

pause работает p → r;

окончание трека приходит как @;

stop работает как s.

И я бы уже не оставлял старую логику play_active == 1/2, а сделал нормальные состояния STOPPED / PLAYING / PAUSED / FINISHED.

Если у тебя исходники плеера — это именно файл «Вставленный текст(1).txt», который ты сейчас загрузил, я могу взять его за основу и переписать конкретно под твой проект, не выдумывая недостающие функции. 



Да напиши полную версию кода авр можно без unishift 

Да. Я бы сделал AVR заново, без UniShift_Out_old.h/.c, но сохранил вашу схему выводов и логику сенсорной ленты.

По коду плеера подтверждается, что он ждёт именно пакет 0xFF + три ASCII-цифры: например, для трека 201 AVR должен передать FF 32 30 31. Плеер затем превращает эти три символа в номер 201 и запускает DFPlayer_Play_track_MP3(201). 


Ниже полный вариант AVR. Я специально убрал зависимость от UniShift и вынес UART в отдельные функции, чтобы не писать напрямую в UDR из разных мест.

#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)
        {