Что-то мне подсказывает, что результатов такого решения здесь не возникнет никогда. Но, безусловно, по очень веской и совершенно непреодолимой причине, никак не зависящей от самой задачи. Чего бы еще от вас ждать.oleg110592 писал(а):Задачу, имхо можно решить за 2 (два) дня и совершенно не надо ждать полтора года.
Программирование STM8
- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
- Реклама
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
по заявкам трудящихся (в личке)
как сделать оцифровку звука с микрофона с помощью АЦП и таймера:
1) настраиваем АЦП (это уже было)
2) настраиваем таймер на частоту оцифровки, приемлемая частота 8кГц, разрешаем прерывания от таймера
3) запускается основной цикл программы, в цикле ждем флаг о готовности данных от АЦП, как только готовы - записываем в SPI-флэш, возвращаемся в начало цикла
4) в обработчике прерывания от таймера запускаем преобразование АЦП, ждем окончания преобразования, как только закончилось, выставляем флаг готовности для основного цикла
копипаст теперь делал из STMовского кода (индусского), там неплохо все сделано (имхо):
не проверялось, могут быть ошибки (я не волшебник. я еще только учусь.), на всякий случай проект иар прилагаю
как сделать оцифровку звука с микрофона с помощью АЦП и таймера:
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
// далее код для записи значения АЦП в буфер
}
}
}- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
В обработчике чего-то ждем? Я отказываюсь верить, чтобы такое могли написать даже индусы. Это вообще какой-то ахтунг за гранью добра и зла.oleg110592 писал(а):4) в обработчике прерывания от таймера запускаем преобразование АЦП, ждем окончания преобразования,
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
паспорт не видел, ссылку уже давал, могу еще раз:могли написать даже индусы
http://brush-v1.googlecode.com/svn/trun ... stm8s_it.c
предлагаем свои, лучшие варианты
проект сделан в конфигурации релиз, переключить на дебаг и настроить - тогда будет дебажить по кодуКак в IAR переключается окно отладчика
сделал проект с дебагом стлинк
буфер будет в spi-флэшА чему равен буфер?
- Реклама
- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
1) АЦП включить при инициализации и больше не выключать.
2) убрать обслуживание ацп вообще из обработчика прерывания
3) нафиг эти ADON-ы, старт АЦП делать по триггеру с таймера
4) флаг окончания преобразования ловить в основном цикле программ.
Это для начала.
2) убрать обслуживание ацп вообще из обработчика прерывания
3) нафиг эти ADON-ы, старт АЦП делать по триггеру с таймера
4) флаг окончания преобразования ловить в основном цикле программ.
Это для начала.
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
- Сообщения: 50
- Зарегистрирован: Чт сен 04, 2014 13:22:44
Их надо в инициализацию вставить?
А триггер в прерывании где найти?
А что за обслуживание АЦП в прерывании?
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
}
А триггер в прерывании где найти?
А что за обслуживание АЦП в прерывании?
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
}
Последний раз редактировалось vash_sa Чт май 28, 2015 20:20:05, всего редактировалось 1 раз.
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
почитайте для начала AN2658 Application note Using the analog to digital converter of the STM8S microcontroller
http://www.st.com/web/en/resource/techn ... 176594.pdf
http://www.st.com/web/en/resource/techn ... 176594.pdf
- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
У таймера (TIM1 в нашем случае) есть выход TRGO. Он не выводится наружу и доступен только блокам другой периферии внутрипроцессорно. На этом выходе можно организовать подачу импульса по какому-то событию таймера. Например, переполнению: TIM1->CR2 |= TIM1_TRGOSOURCE_UPDATE. АЦП можно сконфигурировать так, что он будет следить за выходом TRGO и по фронту импульса начинать преобразование: ADC1->CR2 |= ADC1_CR2_EXTTRIG. В этом случае не требуется исполнения какого-либо кода по запуску АЦП, т.к. АЦП будет запускаться аппаратно по команде таймера. Если таймер сконфигурировать так, чтобы событие возникало с частотой 8 кгц, то с той же дискретностью АЦП будет цифровать сигнал с аналогового входа. Программе остается только в основном цикле проверять флаг окончания преобразования (не надо здесь тупо вставать в цикл ожидания) и по его поднятию забирать результат оцифровки из регистра данных АЦП.
При такой организации алгоритма не расходуется время на выполнение лишних прерываний, запуск ацп и ожидание результата преобразования.
При такой организации алгоритма не расходуется время на выполнение лишних прерываний, запуск ацп и ожидание результата преобразования.
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
можно, от перестановки мест не сильно поменяется, все равно "вручную" запускаем преобразование АЦП и ждем пока закончится, в 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
// далее код для записи значения АЦП в буфер
}
}Подобный способ работы с АЦП описан в AN3280 Application Note Displaying variable voltage on a bar of LEDs using STM8S-DISCOVERY
http://www.st.com/web/catalog/tools/FM1 ... 4/PF257975#
- Сообщения: 50
- Зарегистрирован: Чт сен 04, 2014 13:22:44
А почему нельзя использовать специализированное прерывание № 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.
*/
}
А можно прямо в теле прерывания поместить код заполнения буфера результатом оцифровки?
При этом не засоряя основной цикл.
INTERRUPT_HANDLER(ADC1_IRQHandler, 22)
{
/* In order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction.
*/
}
А можно прямо в теле прерывания поместить код заполнения буфера результатом оцифровки?
При этом не засоряя основной цикл.
- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
Можно использовать. Просто мне не очень симпатично здесь использование прерываний, т.к. под угрозой нехватки мощи процессора тратить время на вход и выход из прерываний это в определенном смысле расточительно. В исходнике этого не видно, но компилятор добавляет код сохранения состояния перед каждым вызовом обработчика прерывания и подобный же код добавляется при выходе из обработчика. Исполнение этого дополнительного кода вызывает потери времени. В не сильно нагруженных приложениях обычно на это не обращают внимания, но здесь бы лучше от этого уйти.
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
Посчитаем загруженность (для первого способа).
От разрешения и времени выборки зависит скорость АЦП. Время необходимое на одно преобразование считается по такой формуле:
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 один байт (вроде можно) или сформировать буфер в ОЗУ микроконтроллера побольше, потом отправить, надо считать...
От разрешения и времени выборки зависит скорость АЦП. Время необходимое на одно преобразование считается по такой формуле:
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 один байт (вроде можно) или сформировать буфер в ОЗУ микроконтроллера побольше, потом отправить, надо считать...
- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
Цифра верная, но расчет у stm8s производится маленько не так: 1 такт уходит на синхронизацию запуска, три такта на выборку и десять тактов на преобразование. Всего 14 тактов. Способа, как либо поменять эту последовательность RM0016 не приводит.oleg110592 писал(а):Например, при частоте тактирования в 8МГц, разрешении в 10 бит и минимальном времени выборки (4 такта) мы можем получить скорость 571.4 кГц = 1.75мкс.
При частоте АЦП в 8мгц на выборку будет приходиться 3 / 8 = 0.375 микросекунды, каковое время будет достаточно, если цифровать, скажем, батарейку. Для более-менее точных преобразований от источника с бОльшим выходным сопротивлением, 375нс, скорее всего, будет недостаточно. С другой стороны, для цифровки речи особой точности может и не потребоваться, но в любом случае, нужно иметь ввиду, что частоту АЦП, возможно, придется снижать.
Лучше сразу исходить из того, что времени не останется ни на что (даже если на начальном этапе это не подтверждается расчетами) и писать код заточенный на максимальное быстродействие. Хуже, если в какой-то момент обнаружится недостаток быстродействия и придется лопатить горы ранее написанного с целью оптимизации.думаю останется еще много времени, в котором микроконтроллер будет просто тупо ждать, пока не наступит следующее прерывание
- Сообщения: 50
- Зарегистрирован: Чт сен 04, 2014 13:22:44
Спасибо за подробный ответ.
SPI будет задействован на 28J60. Второго у stm8s005k6 нет.
У меня есть spi-флэш 25x80avsig. Питание у нее 3.3 - 3.6В.
Куда ее подсоединять, если SPI занят. Можно подключить параллельно кроме cs, а cs на одну из ног stm8.
Давайте к коду буфера поближе. Для начала предлагаю без spi-флэш в ОЗУ. При реализации web-сервера было условие, чтобы объем разового пакета не превышал 200 байт. Может принять размер буфера 128 байт, чтобы он гарантированно входил в 1 пакет?
SPI будет задействован на 28J60. Второго у stm8s005k6 нет.
У меня есть spi-флэш 25x80avsig. Питание у нее 3.3 - 3.6В.
Куда ее подсоединять, если SPI занят. Можно подключить параллельно кроме cs, а cs на одну из ног stm8.
Давайте к коду буфера поближе. Для начала предлагаю без spi-флэш в ОЗУ. При реализации web-сервера было условие, чтобы объем разового пакета не превышал 200 байт. Может принять размер буфера 128 байт, чтобы он гарантированно входил в 1 пакет?
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
все верно по spi можно подключать параллельно, у каждого свой cs.Можно подключить параллельно кроме cs, а cs на одну из ног stm8
код для буфера может быть таким:
Код: Выделить всё
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
}
..........
}- Сообщения: 6452
- Зарегистрирован: Пт сен 13, 2013 13:11:31
Теоретически, размер пакета может быть до 64к, но в целях совместимости и гарантированного прохождения через разнообразное оборудование, есть рекомендации не превышать объем в 508 байт для блока данных. В этой связи нет особого смысла ограничиваться именно 128 байтами и если есть возможность пихать бОльшим куском (200 байт), то стоит ей воспользоваться.vash_sa писал(а):При реализации web-сервера было условие, чтобы объем разового пакета не превышал 200 байт. Может принять размер буфера 128 байт, чтобы он гарантированно входил в 1 пакет?


