На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
  
    > Передача с STM и воспроизведение wav на ПК , STM32F100RB
      Всем привет!

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

      Поясню подробно. Есть схема на базе STM32F100RB. Сама схема содержит микрофон, звук с которого цифруется и подаётся на lan порт ПК.

      Исходник с воспроизведением тонового сигнала:

      ExpandedWrap disabled
        #include "stm32f10x.h"
        #include "stm32f10x_gpio.h"
        #include "stm32f10x_rcc.h"
        #include <stm32f10x_adc.h>
        #include "lan.h"
        #include "counter.h"
        #include "web_if.h"
        #include "ntp.h"
         
        static volatile uint16_t ms_count;
        //static volatile uint32_t second_count;
        static volatile uint32_t ntp_next_update;
        static volatile uint32_t time_offset;
         
        #define NTP_SERVER  inet_addr(192,168,0,11)
        #define TIMEZONE    7
        #define BUFF_MAX    240
         
        int i,j,count =0, k=0;
        int buff_size = BUFF_MAX;
        uint16_t val[BUFF_MAX];
        uint8_t flag_ADC = 1;
        uint16_t sin[240] = {0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024,
                             0, 1024, 2048, 3072, 4095, 3072, 2048, 1024
                        };
         
        GPIO_InitTypeDef GPIO_InitStructure;
        ADC_InitTypeDef ADC_InitStructure;
         
         
        void GPIO_init (void){
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
                GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8 | GPIO_Pin_9 ;   // two LED (guess on what pin!!)
                GPIO_Init(GPIOC, &GPIO_InitStructure);
         
                // input of ADC (it doesn't seem to be needed, as default GPIO state is floating input)
                GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
                GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_1 | GPIO_Pin_4;        // that's ADC1 (PA1 on STM32)
                GPIO_Init(GPIOA, &GPIO_InitStructure);
         
        }
         
        void ADC_init (void){
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
            // define ADC config
                ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
                ADC_InitStructure.ADC_ScanConvMode = DISABLE;
                ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // we work in continuous sampling mode
                ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
                ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
                ADC_InitStructure.ADC_NbrOfChannel = 1;
         
                ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 1,ADC_SampleTime_28Cycles5); // define regular conversion config
         
                ADC_Init ( ADC1, &ADC_InitStructure);   //set config of ADC1
         
                ADC_Cmd (ADC1,ENABLE);  //enable ADC1
         
                //  ADC calibration (optional, but recommended at power on)
                    ADC_ResetCalibration(ADC1); // Reset previous calibration
                    while(ADC_GetResetCalibrationStatus(ADC1));
                    ADC_StartCalibration(ADC1); // Start new calibration (ADC must be off at that time)
                    while(ADC_GetCalibrationStatus(ADC1));
         
                    // start conversion
                        ADC_Cmd (ADC1,ENABLE);  //enable ADC1
                        ADC_SoftwareStartConvCmd(ADC1, ENABLE); // start conversion (will be endless as we are in continuous mode)
                        ADC1->CR2 |= ADC_CR2_ADON;    // запуск преобразования
         
        }
         
        void TIM_init(void){
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);
                TIM6->PSC = 0;
                TIM7->PSC = 0;
                TIM6->ARR = 3000;
                TIM7->ARR = 3000;
                TIM6->DIER |= TIM_DIER_UIE; //разрешаем прерывание от таймера
                TIM7->DIER |= TIM_DIER_UIE; //разрешаем прерывание от таймера
                TIM6->CR1 |= TIM_CR1_CEN; // Начать отсчёт!
                TIM7->CR1 |= TIM_CR1_CEN; // Начать отсчёт!
                NVIC_EnableIRQ(TIM6_DAC_IRQn); //Разрешение TIM6_DAC_IRQn прерывания
                NVIC_EnableIRQ(TIM7_IRQn); //Разрешение TIM7_IRQn прерывания
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
                DAC->CR |= DAC_CR_EN1;
         
        }
         
         
         
        void udp_packet(eth_frame_t *frame, uint16_t len) {
         
            ip_packet_t *ip = (void*)(frame->data);
                udp_packet_t *udp = (void*)(ip->data);
                uint32_t timestamp;
         
                if(udp->to_port == NTP_LOCAL_PORT)
                {
                    if((timestamp = ntp_parse_reply(udp->data, len)))
                    {
                        time_offset = timestamp - second_count;
                        ntp_next_update = second_count + 12UL*60*60;
                        GPIO_SetBits(GPIOC, GPIO_Pin_9);
         
                    }
                }
         
         
        }
         
        uint8_t tcp_listen(uint8_t id, eth_frame_t *frame)
        {
            ip_packet_t *ip = (void*)(frame->data);
            tcp_packet_t *tcp = (void*)(ip->data);
         
            if( (tcp->to_port == htons(80)) ||
                (tcp->to_port == htons(44444)) )
            {
                return 1;
            }
            return 0;
        }
         
        void tcp_opened(uint8_t id, eth_frame_t *frame)
        {
        }
         
        void tcp_closed(uint8_t id, uint8_t reset)
        {
        }
         
        void tcp_data(uint8_t id, eth_frame_t *frame, uint16_t len, uint8_t closing)
        {
            webif_data(id, frame, len);
        }
         
        int main(void)
        {
             // int a =0;
            //  uint32_t loctime;
              //uint8_t s, m, h;
         
              GPIO_init();
              ADC_init ();
              TIM_init();
              lan_init();
              counter_init();
              webif_init();
              GPIO_SetBits(GPIOC, GPIO_Pin_8);
              GPIO_ResetBits(GPIOC, GPIO_Pin_8);
         
         
            while(1)
            {
                lan_poll();
         
         
                if(lan_up()) {
                        GPIO_SetBits(GPIOC, GPIO_Pin_8);
                        ntp_request(NTP_SERVER, sin);
                    } else {
                        GPIO_ResetBits(GPIOC, GPIO_Pin_8);
                    }
         
                //if (!flag_ADC) {
                    //ntp_request(NTP_SERVER, sin);
                    //flag_ADC = 1;
                //}
         
                //ntp_request(NTP_SERVER);
                /*if(second_count >= ntp_next_update)
                        {
                            if(!ntp_request(NTP_SERVER))
                                ntp_next_update = second_count+2;
                            else
                                ntp_next_update = second_count+2;
                        }*/
                //loctime = time_offset+second_count + 60UL*60*TIMEZONE;
         
         
            }
            return 0;
         
        }
         
        /*Обработчик прерывания от таймера 6 */
        void TIM6_DAC_IRQHandler(void) {
          TIM6->SR &= ~TIM_SR_UIF; //Сбрасываем флаг UIF
         
          //if (count<buff_size){
              //DAC->CR &= ~DAC_CR_EN1;
              //ADC1->CR2 |= ADC_CR2_ADON;    // запуск преобразования
              //while (!(ADC1->SR & ADC_SR_EOC)); // ждем конца преобразования
              //val[count++] = ADC_GetConversionValue(ADC1);
          //}
          //else {
              //ADC1->CR2 &= ~ADC_CR2_ADON;
              //DAC->CR |= DAC_CR_EN1;
         
              DAC->DHR12R1 = sin[k++]; //10;
              //DAC->DHR12R1 = val[k++]/10;
         
         
          //}
          //else DAC->DHR12R1 = sin[k++];
          //DAC->DHR12R1 = val[k++];
         
          if (k == buff_size){
              k = 0;
            }
         
          //sin[i++]; //Запихиваем в ЦАП очередной элемент массива
          //if (i==8) i=0; //Если вывели в ЦАП все 32 значения то начинаем заново
         
        }
        /*Обработчик прерывания от таймера 6 */
        void TIM7_IRQHandler(void) {
         
          TIM7->SR &= ~TIM_SR_UIF; //Сбрасываем флаг UIF
          val[count++] = ADC_GetConversionValue(ADC1);
          if (count == buff_size){
              //ntp_request(NTP_SERVER, val);
              flag_ADC = 0;
              count = 0;
            }
         
         
        }


      Контроллер, как мне объяснили, работает в 12 битном режиме. То есть, как я это понимаю, используется Int16 число, но, по 4 из 16 битам выравнивается нулями и лишь 12 бит содержат информацию.

      Сам контроллер работает как часы - свой звук считывает, воспроизводит, передаёт. Приложение клиента (ПК) так же, сам свой формат читает, воспроизводит, передаёт.

      Параметры которые я читаю компьютером шарпом:

      ExpandedWrap disabled
        private int samplesPerSecond = 8000;
        private int bitPerSecond     = 16;
        private int channels         = 1;
        private int bufferSize       = 200;


      По сути я читаю Int16 - для этого просто последовательно извлекаю byte[] из 2 элементов.

      Наколякал мини "осцилограф" на шарпе - всё прекрасно показывает (на стороне компьютера). Приступаю к тесту - получаю дикие помехи. Пакеты с контроллера доходят на клиент. Клиент их успешно отображает , воспроизводит, но:

      1. звук на порядок ниже по тону (это, как я понял, связанно с тем, что я оперирую числами на 16 бит, это в диапозоне 0...65536, а контроллер, работает с 12 битами, что можно представить диапазоном 0...4096 - была идея "размазать" амплитуду домножая каждую величину на отношение 65536/4096, но, боюсь что с голосом такое не прокатит...).
      2. появляются пробелы, звук входит порциями, и динамик хрипит потом молчит (как я понял, это связанно с длинной буфера?)
      3. я так и не услышал никакого голоса на ПК когда говорю в контроллер. Может ли это быть связанно с настолько сильными искажениями и занижением тона, что сигнал есть, а я его просто не слышу? Или не факт, и сигнала может действительно не быть? Тогда что я не так расшифровываю? (по сути, просто передаю точку как UInt16, получаю точку как byte[] длинной 2)

      Ни в какие кодировки, преобразования и кодеки пока не влазил, всё в чистом wave без сжатия!

      Прикрепляю код, который насишарпил.
      Прикреплённый файлПрикреплённый файлWavUDP.rar (663,77 Кбайт, скачиваний: 243)
        Цитата
        звук на порядок ниже по тону

        Не угадал частоту дискретизации(?)
        Цитата
        По сути я читаю Int16 - для этого просто последовательно извлекаю byte[] из 2 элементов.

        Скажи в микрофон "стодвадцатьпять_стодвадцатьшесть_стодвадцатьсемь", залей сырые данных куда-нибудь.
        Сообщение отредактировано: Prince -
          Цитата
          Скажи в микрофон "стодвадцатьпять_стодвадцатьшесть_стодвадцатьсемь", залей сырые данных куда-нибудь.

          До ночи разбирался (заметил что файлик то у меня формируется черезчур маленький!! а там wav без зжатия, и по всем прикидкам, даже с хреновым качеством, фраза "стодвадцатьпять_стодвадцатьшесть_стодвадцатьсемь" ну уж никак не могла весить 200-240 БАЙТ)

          В общем, нашёл небольшую ошибку... по протоколу udp контроллер отсылал буфер в 1024 байт, а я вижу сторонним софтом, что приходит пакет 60 байт. (Это исправил)

          Постараюсь как можно быстрее сделать правильную запись. Так же, пытаюсь передать тоновый сигнал, который мифическим образом, появляется раз в 10-18 секунд на 0,1 секунды, хотя контроллер шлёт его непрерывно... куда уходит больше 99% информации даже ума не приложу :(

          Насчёт сети: есть роутер, он пробрасывает порты для компов (их несколько) и выдаёт адрес контроллеру. Самое интересное, если бы я просто терял данные, мог бы винить неполадки сети или роутера, но, данные то приходят! Просто они вообще не имеют отношения к звуковой информации (либо, сильнейшие шумы, хотя, отправляю то синусоиду тонового сигнала...)

          Ещё, есть предположение, что я неправильно читаю два байта от uint16 - может ли быть такое, что я переставляю их местами? Как они вообще должны себя вести у сишного uint16?

          Например, в шарпе я формирую сообщение на компе, где слева старший байт со старшими битами, справа - младший байт с младшими битами:

          1024 = 04 00
          могут ли быть такие варианты декодирования 1024:

          00 04? (перестановка байт)
          00 01? (перестановка байт и бит)
          01 00? (перестановка бит)
          Сообщение отредактировано: VisualProg -
            Цитата
            Ещё, есть предположение, что я неправильно читаю два байта от uint16 - может ли быть такое, что я переставляю их местами? Как они вообще должны себя вести у сишного uint16?

            Ещё может быть выравнивание данных влево и вправо(судя по коду, контроллер использует выравнивание вправо, т.е., значащими являются младшие 12 бит), причём "неиспользуемые" биты могуть быть как нулевыми так и забиты некой служебной инфой.
            Если удасться получить нормальный поток аудиоданных с устройства, возможно, проблема с форматом решится "автоматически", т.е., будет там int16 без ньюансов. Если нет, залейте потом куда-нибудь и скиньте ссылку.
            Мне проще разбираться с форматом присылаемых данных, чем искать причину, почему программа не работает. :scratch:
            Цитата
            Постараюсь как можно быстрее сделать правильную запись. Так же, пытаюсь передать тоновый сигнал, который мифическим образом, появляется раз в 10-18 секунд на 0,1 секунды, хотя контроллер шлёт его непрерывно... куда уходит больше 99% информации даже ума не приложу

            Судя по вашему описанию, проблема пока не столько в формате данных, сколько в неработоспоспособности программы в целом... :-?
            Сообщение отредактировано: Prince -
              Основная проблема, как выяснилось, была в том, что у контроллера в реализации протокола была допущена ошибка! (фрейм пакета формировался с избытком информации). Отсюда, почти половина данных wav потока - одни и те же бинарные данные, связанные с заголовком пакета :wall: . Из-за всего этого и были непонятные "звуки". В целом качество передаваемой волны оставляет желать лучшего, но в асинхронном режиме, всё работает более менее сносно. Правда, такой контроллер спустя время начинает захлёбываться в трафике. В синхронном режиме работы:

              0. Контроллер копит порцию данных.

              1. Контроллер отправляет данные серверу.
              2. Контроллер копит данные.
              3. Сервер читает входные данные.
              4. Данные заносятся в очередь.
              5. Сервер отсылает свою порцию данных контроллеру.
              6. Сервер копит данные + переходит в пункт 1

              Сервер воспроизводит накопленный буфер, для меня задержка в 1-2 сек. не существенна. Но, возникает непонятная ошибка (это ошибка не по теме, связная с моим криворуким программированием на шарпе) - чем больше очередь данных у сервера, тем медленней идёт воспроизведение, создаётся эффект замедленного воспроизведения (тон понижается незаметно, но во времени растягивается ооочень сильно, 1 сек. может длится от 5 сек. до нескольких минут.). Использую List<byte>, насколько знаю, коллекции достаточно быстрые. Для воспроизведения - создаю поток, преобразую часть коллекции в массив (буквально 1Кб. не больше), и в потоке воспроизвожу байтовый массив на колонки.

              Контроллер всё успевает воспроизвести и отослать без всяких замедлений и задержек. Так что, основная проблема решилась и вопрос тоже).

              Большое спасибо за помощь)
                я бы сразу в массив сбрасывал на сервере, и воспроизводил. Там просто с указателями чтения и записи нужно правильно все сделать. А сервер на чем написан?
                  Цитата
                  я бы сразу в массив сбрасывал на сервере, и воспроизводил.

                  я прикреплял исходник, всё так и реализованно)

                  Цитата
                  А сервер на чем написан?

                  C#
                    А это где, на контроллере?
                    Цитата VisualProg @
                    Использую List<byte>, насколько знаю, коллекции достаточно быстрые.
                      Цитата DIS @
                      А это где, на контроллере?

                      :blink: сервер это. Контроллер на сях.

                      Поясню. Мне приходит массив байт. Я его добавляю в буфер List<byte>, далее, сюжет идёт по двум сценариям:

                      1. Если буфер достаточно большой (около 512 байт) - создаётся поток и всё пускается на воспроизведение
                      2. Если буфер менее 512 байт - сервер ждёт следующую порцию данных

                      Для чего это сделано - физически, я не могу ускорить работу контроллера, а он, не могёт выдавать информацию с большЕй скоростью, если я воспроизвожу полученные 128-256 байт - буфер очищатся быстрее чем приходит следующая порция данных. Получаю постоянные "отсечки" или пробелы. Поэтому коплю данные в необходимом объёме.

                      Или я что то не так делаю? Просто других решений не придумал :scratch:
                      Сообщение отредактировано: VisualProg -
                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0,0352 ]   [ 18 queries used ]   [ Generated: 19.03.24, 04:55 GMT ]