Страница 36 из 130

Re: Программирование STM8

Добавлено: Чт май 28, 2015 12:35:44
a5021
oleg110592 писал(а):Задачу, имхо можно решить за 2 (два) дня и совершенно не надо ждать полтора года.
Что-то мне подсказывает, что результатов такого решения здесь не возникнет никогда. Но, безусловно, по очень веской и совершенно непреодолимой причине, никак не зависящей от самой задачи. Чего бы еще от вас ждать.

Re: Программирование STM8

Добавлено: Чт май 28, 2015 18:07:34
oleg110592
по заявкам трудящихся (в личке)
как сделать оцифровку звука с микрофона с помощью АЦП и таймера:
1) настраиваем АЦП (это уже было)
2) настраиваем таймер на частоту оцифровки, приемлемая частота 8кГц, разрешаем прерывания от таймера
3) запускается основной цикл программы, в цикле ждем флаг о готовности данных от АЦП, как только готовы - записываем в SPI-флэш, возвращаемся в начало цикла
4) в обработчике прерывания от таймера запускаем преобразование АЦП, ждем окончания преобразования, как только закончилось, выставляем флаг готовности для основного цикла
копипаст теперь делал из STMовского кода (индусского), там неплохо все сделано (имхо):
Спойлер

Код: Выделить всё

#include "stm8s.h"

/* TIM2 define */
#define TIMER_REL_VAL ((uint16_t)0x007D)  /*Auto-reload value to generate a time delay of 125us, after which
                                            overflow interrupt is asserted
                                            Calculations:
                                                fcpu=16MHz, Presc=16
                                                Time Period of counter is 16/(16*10^6) = 1 µs
                                                Time duration required = 125us
                                                Therefore auto-reload counter value = 125us/1µs = 007D*/

uint16_t adc_data;
uint8_t flags;

#define ADC_DATA_READY (uint8_t)(1<<0) 

INTERRUPT_HANDLER(TIM2_UPD_OVF_IRQHandler, 13) // TIM2 interrupt routine
{
    /*This interrupt routine is executed after every 125us approx*/

    /* Interrupt generated by UIF */
    ADC1->CR1 |= ADC1_CR1_ADON;     //ADC powered up
    ADC1->CR1 |= ADC1_CR1_ADON;     //begin conversion

    while (!(ADC1->CSR & ADC1_CSR_EOC)) {}; //check for the EOC

    adc_data = ADC1->DRL;           //must read LSBs first
    adc_data |= ADC1->DRH << 8;

    ADC1->CR1 |= ~ADC1_CR1_ADON;    //ADC powered down

    TIM2->SR1 &= ~TIM2_SR1_UIF;     /* Clear UIF flag */
    
    flags |= ADC_DATA_READY;        // set ADC ready flag
}

void TIM2_init(void) // Настройка таймера 2
{
    TIM2->PSCR = 4;                             // init divider register/16
    TIM2->ARRH = (uint8_t)(TIMER_REL_VAL >> 8); // init ARR
    TIM2->ARRL = (uint8_t)(TIMER_REL_VAL);
    TIM2->CR1 |= TIM2_CR1_URS;                  // update on overflow
    TIM2->CR1 |= TIM2_CR1_ARPE;                 // enable auto-reload preload, counter will be free-running
    TIM2->IER = TIM2_IER_UIE;                   // enable update on overflow interrupt
}

void ADC_init( void ) // Настройка АЦП
{
	//AIN6 PIN3
    /* Clear the align bit */
    ADC1->CR2 &= (uint8_t)(~ADC1_CR2_ALIGN);
    /* Configure the data alignment */
    ADC1->CR2 |= (uint8_t)0x08; //Right
    /* Clear the SPSEL bits */
    ADC1->CR1 &= (uint8_t)(~ADC1_CR1_SPSEL);
    /* Select the prescaler division factor according to ADC1_PrescalerSelection values */
    ADC1->CR1 |= (uint8_t)0x60; //fcpu/12 
    /* Set the single conversion mode */
    ADC1->CR1 &= (uint8_t)(~ADC1_CR1_CONT);
    /* Clear the ADC1 channels */
    ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
    /* Select the ADC1 channel */
    ADC1->CSR |= (uint8_t)(0x06);
    /* Disables the ADC1 Schmitt Trigger on a selected channel */
    ADC1->TDRL |= (uint8_t)((uint8_t)0x01 << (uint8_t)0x06); // Schmitt trigger disable on 6 chanel
}

void main( void )
{
    ADC_init(); 
    TIM2_init();
    flags = 0;
    asm("rim");
    while (1)
    {   
        while (!(flags & ADC_DATA_READY)) // wait to set flag adc ready
        {
            flags &= ~ADC_DATA_READY;        //clear ADC ready flag
            // далее код для записи значения АЦП в буфер
        }
    }
}
не проверялось, могут быть ошибки (я не волшебник. я еще только учусь.), на всякий случай проект иар прилагаю
STM8Voip.zip
(175.05 КБ) 185 скачиваний

Re: Программирование STM8

Добавлено: Чт май 28, 2015 18:38:20
a5021
oleg110592 писал(а):4) в обработчике прерывания от таймера запускаем преобразование АЦП, ждем окончания преобразования,
В обработчике чего-то ждем? Я отказываюсь верить, чтобы такое могли написать даже индусы. Это вообще какой-то ахтунг за гранью добра и зла.

Re: Программирование STM8

Добавлено: Чт май 28, 2015 18:57:43
vash_sa
А чему равен буфер?

Re: Программирование STM8

Добавлено: Чт май 28, 2015 19:10:30
oleg110592
могли написать даже индусы
паспорт не видел, ссылку уже давал, могу еще раз:
http://brush-v1.googlecode.com/svn/trun ... stm8s_it.c
предлагаем свои, лучшие варианты
Как в IAR переключается окно отладчика
проект сделан в конфигурации релиз, переключить на дебаг и настроить - тогда будет дебажить по коду
сделал проект с дебагом стлинк
STM8Voip.zip
(321.54 КБ) 175 скачиваний
А чему равен буфер?
буфер будет в spi-флэш

Re: Программирование STM8

Добавлено: Чт май 28, 2015 19:40:07
a5021
1) АЦП включить при инициализации и больше не выключать.
2) убрать обслуживание ацп вообще из обработчика прерывания
3) нафиг эти ADON-ы, старт АЦП делать по триггеру с таймера
4) флаг окончания преобразования ловить в основном цикле программ.

Это для начала.

Re: Программирование STM8

Добавлено: Чт май 28, 2015 19:43:36
oleg110592
отлично, это будет домашнее задание автору

Re: Программирование STM8

Добавлено: Чт май 28, 2015 19:44:37
vash_sa
А можно в коде поправить? А если не справлюсь?
А ADON ацп включается?

Re: Программирование STM8

Добавлено: Чт май 28, 2015 19:51:17
oleg110592
Пробуйте. Возникнут вопросы - постараемся помочь.

Re: Программирование STM8

Добавлено: Чт май 28, 2015 19:55:42
vash_sa
Их надо в инициализацию вставить?
А триггер в прерывании где найти?
А что за обслуживание АЦП в прерывании?

INTERRUPT_HANDLER(TIM2_UPD_OVF_IRQHandler, 13) // TIM2 interrupt routine
{
/*This interrupt routine is executed after every 125us approx*/

/* Interrupt generated by UIF */

while (!(ADC1->CSR & ADC1_CSR_EOC)) {}; //check for the EOC - это перенести в основной цикл можно?

adc_data = ADC1->DRL; //must read LSBs first - это запись результата оцифровки?
adc_data |= ADC1->DRH << 8;


TIM2->SR1 &= ~TIM2_SR1_UIF; /* Clear UIF flag */

flags |= ADC_DATA_READY; // set ADC ready flag

}

Re: Программирование STM8

Добавлено: Чт май 28, 2015 20:07:45
oleg110592
почитайте для начала AN2658 Application note Using the analog to digital converter of the STM8S microcontroller
http://www.st.com/web/en/resource/techn ... 176594.pdf

Re: Программирование STM8

Добавлено: Чт май 28, 2015 21:40:40
a5021
У таймера (TIM1 в нашем случае) есть выход TRGO. Он не выводится наружу и доступен только блокам другой периферии внутрипроцессорно. На этом выходе можно организовать подачу импульса по какому-то событию таймера. Например, переполнению: TIM1->CR2 |= TIM1_TRGOSOURCE_UPDATE. АЦП можно сконфигурировать так, что он будет следить за выходом TRGO и по фронту импульса начинать преобразование: ADC1->CR2 |= ADC1_CR2_EXTTRIG. В этом случае не требуется исполнения какого-либо кода по запуску АЦП, т.к. АЦП будет запускаться аппаратно по команде таймера. Если таймер сконфигурировать так, чтобы событие возникало с частотой 8 кгц, то с той же дискретностью АЦП будет цифровать сигнал с аналогового входа. Программе остается только в основном цикле проверять флаг окончания преобразования (не надо здесь тупо вставать в цикл ожидания) и по его поднятию забирать результат оцифровки из регистра данных АЦП.

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

Re: Программирование STM8

Добавлено: Пт май 29, 2015 06:23:56
oleg110592
это перенести в основной цикл можно?
можно, от перестановки мест не сильно поменяется, все равно "вручную" запускаем преобразование АЦП и ждем пока закончится, в adc_data результат

Код: Выделить всё

INTERRUPT_HANDLER(TIM2_UPD_OVF_IRQHandler, 13) // TIM2 interrupt routine
{
    /*This interrupt routine is executed after every 125us approx*/
    TIM2->SR1 &= ~TIM2_SR1_UIF;     /* Clear UIF flag */
    flags |= ADC_DATA_READY;        // set ADC ready flag
}

void main( void )
{
......
        while (!(flags & ADC_DATA_READY)) // wait to set flag adc ready
        {
            flags &= ~ADC_DATA_READY;        //clear ADC ready flag
    
            ADC1->CR1 |= ADC1_CR1_ADON;     //ADC powered up
            ADC1->CR1 |= ADC1_CR1_ADON;     //begin conversion

            while (!(ADC1->CSR & ADC1_CSR_EOC)) {}; //check for the EOC

            adc_data = ADC1->DRL;           //must read LSBs first
            adc_data |= ADC1->DRH << 8;

            ADC1->CR1 |= ~ADC1_CR1_ADON;    //ADC powered down
            
            // далее код для записи значения АЦП в буфер
        }
}
способ, предложенный a5021 намного лучше, только в таком случае вроде можно использовать только TIM1, этот таймер с расширенными возможностями, жалко его использовать, но в этой задаче он как бы больше и не нужен.
Подобный способ работы с АЦП описан в AN3280 Application Note Displaying variable voltage on a bar of LEDs using STM8S-DISCOVERY
http://www.st.com/web/catalog/tools/FM1 ... 4/PF257975#

Re: Программирование STM8

Добавлено: Пт май 29, 2015 10:24:27
vash_sa
А почему нельзя использовать специализированное прерывание № 22?

INTERRUPT_HANDLER(ADC1_IRQHandler, 22)
{
/* In order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction.
*/
}

А можно прямо в теле прерывания поместить код заполнения буфера результатом оцифровки?
При этом не засоряя основной цикл.

Re: Программирование STM8

Добавлено: Пт май 29, 2015 10:31:17
a5021
Можно использовать. Просто мне не очень симпатично здесь использование прерываний, т.к. под угрозой нехватки мощи процессора тратить время на вход и выход из прерываний это в определенном смысле расточительно. В исходнике этого не видно, но компилятор добавляет код сохранения состояния перед каждым вызовом обработчика прерывания и подобный же код добавляется при выходе из обработчика. Исполнение этого дополнительного кода вызывает потери времени. В не сильно нагруженных приложениях обычно на это не обращают внимания, но здесь бы лучше от этого уйти.

Re: Программирование STM8

Добавлено: Пт май 29, 2015 10:52:20
oleg110592
Посчитаем загруженность (для первого способа).
От разрешения и времени выборки зависит скорость АЦП. Время необходимое на одно преобразование считается по такой формуле:
Tconv = (ts+n)/fADC
Из этого легко получить максимальную скорость работы АЦП:
ADCSpeed = fADC/(ts+n)
В этих формулах
fADC — Частота тактового сигнала на АЦП
ts — время выборки (в тактах)
n — разрешение АЦП
Например, при частоте тактирования в 8МГц, разрешении в 10 бит и минимальном времени выборки (4 такта) мы можем получить скорость 571.4 кГц = 1.75мкс.

прерывания происходят каждые 125мкс, после обработки прерывания стартуем:
преобразование займет 1.75мкс (вот это время микроконтроллера мы тратим впустую)
добавим там на всякие проверки и нахождение в прерывании 1.75мкс+?=25мкс (думаю сильно завышено)
итого до следующего прерывания у нас есть время 125-25=100мкс
микроконтроллер работает на частоте 16МГц - один такт 0.0625мкс
у нас есть 1600 тактов
за это время (100мкс) мы успеем записать оцифрованные данные в SPI-флэш
думаю останется еще много времени, в котором микроконтроллер будет просто тупо ждать, пока не наступит следующее прерывание
может где и ошибся
может можно будет обойтись и без флэш буфера - отправлять по UDP один байт (вроде можно) или сформировать буфер в ОЗУ микроконтроллера побольше, потом отправить, надо считать...

Re: Программирование STM8

Добавлено: Пт май 29, 2015 13:10:36
a5021
oleg110592 писал(а):Например, при частоте тактирования в 8МГц, разрешении в 10 бит и минимальном времени выборки (4 такта) мы можем получить скорость 571.4 кГц = 1.75мкс.
Цифра верная, но расчет у stm8s производится маленько не так: 1 такт уходит на синхронизацию запуска, три такта на выборку и десять тактов на преобразование. Всего 14 тактов. Способа, как либо поменять эту последовательность RM0016 не приводит.

При частоте АЦП в 8мгц на выборку будет приходиться 3 / 8 = 0.375 микросекунды, каковое время будет достаточно, если цифровать, скажем, батарейку. Для более-менее точных преобразований от источника с бОльшим выходным сопротивлением, 375нс, скорее всего, будет недостаточно. С другой стороны, для цифровки речи особой точности может и не потребоваться, но в любом случае, нужно иметь ввиду, что частоту АЦП, возможно, придется снижать.
думаю останется еще много времени, в котором микроконтроллер будет просто тупо ждать, пока не наступит следующее прерывание
Лучше сразу исходить из того, что времени не останется ни на что (даже если на начальном этапе это не подтверждается расчетами) и писать код заточенный на максимальное быстродействие. Хуже, если в какой-то момент обнаружится недостаток быстродействия и придется лопатить горы ранее написанного с целью оптимизации.

Re: Программирование STM8

Добавлено: Пт май 29, 2015 13:19:56
vash_sa
Спасибо за подробный ответ.
SPI будет задействован на 28J60. Второго у stm8s005k6 нет.
У меня есть spi-флэш 25x80avsig. Питание у нее 3.3 - 3.6В.
Куда ее подсоединять, если SPI занят. Можно подключить параллельно кроме cs, а cs на одну из ног stm8.
Давайте к коду буфера поближе. Для начала предлагаю без spi-флэш в ОЗУ. При реализации web-сервера было условие, чтобы объем разового пакета не превышал 200 байт. Может принять размер буфера 128 байт, чтобы он гарантированно входил в 1 пакет?

Re: Программирование STM8

Добавлено: Пт май 29, 2015 13:46:07
oleg110592
Можно подключить параллельно кроме cs, а cs на одну из ног stm8
все верно по spi можно подключать параллельно, у каждого свой cs.
код для буфера может быть таким:

Код: Выделить всё

void main( void )
{
  uint8_t buffer[128];
  uint8_t buffer_counter = 0;
..........
            // далее код для записи значения АЦП в буфер
            buffer[buffer_counter++] = (uint8_t)adc_data;
            if(buffer_counter == 128)
            {
                buffer_counter = 0;
                //тут должен быть код для для отправки буфера по UDP 
            }
..........
}

Re: Программирование STM8

Добавлено: Пт май 29, 2015 14:12:49
a5021
vash_sa писал(а):При реализации web-сервера было условие, чтобы объем разового пакета не превышал 200 байт. Может принять размер буфера 128 байт, чтобы он гарантированно входил в 1 пакет?
Теоретически, размер пакета может быть до 64к, но в целях совместимости и гарантированного прохождения через разнообразное оборудование, есть рекомендации не превышать объем в 508 байт для блока данных. В этой связи нет особого смысла ограничиваться именно 128 байтами и если есть возможность пихать бОльшим куском (200 байт), то стоит ей воспользоваться.