shematehnik

Глюк Stm8S003 Или Ошибка В Коде?

17 сообщений в этой теме

shematehnik    1

При написании программы под контроллер STM8S003 столкнулся с такой проблемой.

Когда я работаю с одним каналом АЦП, то все работает правильно, измеренные показания соответствуют действительности.

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

По отдельности они работают отлично. Режим выбрал "Single mode". Запускаю преобразования битом ADON. По прерыванию EOC забираю значение из регистра данных, выбираю следующий канал в ADC_CSR_bit.CH и снова битом ADON запускаю новое преобразование.

Что не так я делаю или есть какой-то заводской глюк. Другие режимы мне не подходят.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Быстрый заказ печатных плат

Полный цикл производства PCB по низким ценам!

  • x
    мм
Заказать Получить купон на $5.00
Yurkin2015    247

Вход АЦП не успевает перезарядиться на новое напряжение другого канала. Видимо источник измеряемого напряжения довольно высокоомный.

Надо бы между выборкой следующего канала и стартом ADON добавить небольшую задержку, типа 100мкс, если не жалко.

Либо уменьшить скорость работы АЦП, изменив биты SPSEL. Так как после каждого ADON=1, АЦП сначала 3 такта накапливает сигнал на входе, а потом 10 тактов конвертирует то, что накопилось. Уменьшая тактовую частоту АЦП, тем самым увеличиваете время выборки входного сигнала.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Пробовал в SPSEL записывать максимально возможное значение. Все равно это не помогло. Пробовал один и тот же канал выбирать подряд по 10 раз, потом второй (ну грубо говоря сразу 10 раз один канал сканировать потом другой), но все равно показания "прыгают".

На первый канал подключена клавиатура с 5-ю кнопками, которая представляет собой делитель напряжения и в зависимости от нажатой кнопки соотношение меняется, а соответственно и напряжение на входе АЦП разное.

В исходном состоянии все кнопки отпущены и вход подтянут к земле резистором 1МОм. На второй вход АЦП поступает с делителя выходное напряжение источника. Так вот с этого входа максимальное показание достигает где 500 отсчетов, причем при вращении потенциометра показания АЦП периодически проходят через 0 и снова нарастают. Или может показывать 500 единиц, но если при этом на втором канале АЦП нажать кнопку, то показания могут на несколько сотен уменьшиться. По отдельности что клавиатура, что измеритель выходного напряжения работают отлично...

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Ну, сделайте задержку перед стартом АЦП после переключения канала. Это проще проверить, чем строить хардварные буферные какскады на ОУ перед каналами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Ну, сделайте задержку перед стартом АЦП после переключения канала. Это проще проверить, чем строить хардварные буферные какскады на ОУ перед каналами.

Задержка тоже не помогает. Можно было бы попробовать применить режим сканирования, но у меня другие входы AIN работают в цифровом режиме

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Видимо без схемы и кода в студию тут не обойтись. Иначе какое-то гадание получается.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

//******************************************************************************
//Инициализация АЦП
//******************************************************************************
void adc_init(){
//Отключение триггеров Шмитта по всем каналам
ADC_TDRL = 0xff;
ADC_TDRH = 0xff;
ADC_CR1_bit.SPSEL = 0x07; //Предделитель Fosc/18
ADC_CR1_bit.CONT = 0;	 //Одиночный режим конвертирования
ADC_CSR_bit.EOCIE = 0;	 //Выключить прерывания по завершению преобразования
ADC_CSR_bit.AWDIE = 0;	 //Выключить прерывания watchdog
ADC_CR2 = 0;
ADC_CR2_bit.ALIGN = 1;	 //Правое выравнивание результата преобразования
CLK_PCKENR2 &= ~0x08;	 //Выключаем тактирование АЦП (по умолчанию вкл)

adc_stage = ADC_STG_INIT;
}
//******************************************************************************
//Запуск преобразований АЦП
//******************************************************************************
char adc_start(){
//Включаем тактирование АЦП
CLK_PCKENR2 |= 0x08;	

//Если нет списка каналов, то не запускаемся
if(adc_channels_list.list == NULL)
return 0;
else {
adc_channels_list.current = adc_channels_list.list;
ADC_CSR_bit.CH = adc_channels_list.current->channel;
}

ADC_CSR_bit.EOCIE = 1;	 //Включить прерывания по завершению преобразования
ADC_CR1_bit.ADON = 1;	 //Первая установка бита выводит из спящего реж.
ADC_CR1_bit.ADON = 1;

adc_stage = ADC_STG_STRT;

return 1;
}

..............

//******************************************************************************
//Обработка результатов измерения АЦП
//******************************************************************************
#pragma vector=ADC1_EOC_vector
__interrupt void adc_conv_done(void){

//Завершение преобразования
if(ADC_CSR_bit.EOC){
ADC_CSR_bit.EOC = 0;
if(adc_channels_list.list){
//Считываем показания АЦП и запоминаем их в переменную
*adc_channels_list.current->variable = ADC_DRH;
*adc_channels_list.current->variable <<= 8;
*adc_channels_list.current->variable |= ADC_DRL;
//Выбираем следующий канал АЦП
adc_channels_list.current = adc_channels_list.current->next_channel;
ADC_CSR_bit.CH = adc_channels_list.current->channel;
}
}

//Запуск следующего преобазования
ADC_CR1_bit.ADON = 1;
}

Схема тут не добавляется

Изменено пользователем shematehnik

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Вот максимально упростил программу для работы только с АЦП. Проблема пока осталась.

#include <iostm8s003f3.h>
#include <intrinsics.h>
unsigned int adc_ain5, adc_ain6;
void adc_init(){

   CLK_PCKENR2 |= 0x08;	    //Включаем тактирование АЦП (по умолчанию вкл)

   //Отключение триггеров Шмитта по всем каналам
   //ADC_TDRL = 0xff;
   //ADC_TDRH = 0xff;
   ADC_CR1_bit.SPSEL = 0x07;  //Предделитель Fosc/18
   ADC_CR1_bit.CONT = 0;	  //Одиночный режим конвертирования
   ADC_CSR_bit.EOCIE = 0;	 //Выключить прерывания по завершению преобразования
   ADC_CSR_bit.AWDIE = 0;	 //Выключить прерывания watchdog
   ADC_CR2 = 0;
   ADC_CR2_bit.ALIGN = 1;	 //Правое выравнивание результата преобразования

   ADC_CR1_bit.ADON = 1;
   ADC_CR1_bit.ADON = 1;
}
void adc_conv(){
 //Выбор канала
 if(ADC_CSR_bit.CH != 5)
   ADC_CSR_bit.CH = 5;
 else
   ADC_CSR_bit.CH = 6;

 for(unsigned long int delay = 0; delay < 0xfff; delay++);

 //начало преобразования
 ADC_CR1_bit.ADON = 1;			  

 //ждем окончания преобразования
 while(!ADC_CSR_bit.EOC);	  

 //сброс флага окончания
 ADC_CSR_bit.EOC = 0;			   

 //Забираем результат
 if(ADC_CSR_bit.CH == 5){
  adc_ain5 = ADC_DRH << 8;
  adc_ain5 |= ADC_DRL;
 } else {
   adc_ain6 = ADC_DRH << 8;
   adc_ain6 |= ADC_DRL;
 }
}
void main()
{
 adc_init();
 while(1){
   adc_conv();
 }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Оптимизатор пустой цикл выбрасывает? Попробуйте такую задержку поставить - задержка на время преобразования, просто одно холостое преобразование :

ADC_CR1_bit.ADON = 1;

while(!ADC_CSR_bit.EOC);

ADC_CSR_bit.EOC = 0;

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Оптимизацию я отключил пока:

// 27
// 28 for(unsigned long int delay = 0; delay < 0xfff; delay++);
??adc_conv_1:
 CLRW	 X
 LDW	 S:?w1, X
 LDW	 S:?w0, X
??adc_conv_2:
 LDW	 X, S:?w0
 CPW	 X, #0x0
 JRNE	 L:??adc_conv_3
 LDW	 X, S:?w1
 CPW	 X, #0xfff
??adc_conv_3:
 JRNC	 L:??adc_conv_4
 CALL	 L:?inc32_l0_l0
 JRA	 L:??adc_conv_2

Сделал запуск АЦП по внешнему триггеру от таймера 1 с периодом запуска 20 мс, вроде все работает отлично, но это в режиме "Single scan mode"

Сейчас попробую сделать запуск от таймера, но с ручным переключением каналов, т.е. "Single mode".

Попробовал, такая же беда, как и без таймера

Вообщем ST Electronics где-то накосячили... Видимо режим Single mode предназначен только для одного входа, но в то же время нет других режимов, которые позволяли сканировать только выбранные входы, можно только сразу все, что неудобно из-за такой вот вещи:

When using scan mode, it is not possible to use channels AIN0 to AINn in output mode

because the output stage of each channel is disabled when it is selected by the ADC

multiplexer.

А это как раз мой случай, когда другие AIN используются как цифровые выходы...

Мда.. еще ни с одним контроллером не было столько секаса. Видно сильно торопились выпустить самый дешевый мк.

Изменено пользователем shematehnik

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Тут дело в следующем, видимо. У вашего АЦП время выборки 3 тика, то есть на это время открываются ворота и заряжается входная ёмкость АЦП. Потом ворота закрываются и начинается преобразование. Вот эту внутренную ёмкость АЦП и не успевает зарядить источник напряжения. Поэтому никакие задержки тут не помогут. Когда один канал опрашивается через некоторое количество опросов напряжение на ёмкости добивается до правильного. А когда каналы переключаются - получается фигня.

Тут есть выход. Надо на каждый вход АЦП повесить дополнительную ёмкость , типа 10 нФ, со входа на землю. Это значительно ускорит заряд внутренней ёмкости выборки-хранения и даст правильный результ сразу.

Изменено пользователем Yurkin2015

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Оставил сканирующий режим АЦП. Влияние режима на цифровые входы "на глаз" не замечено) Пусть будет пока так. В схему внести изменения не могу, это готовое устройство со своей прошивкой. Просто нужно изменить алгоритм работы. Все равно тогда не понятно: как в сканирующем режиме открытия "ворот" хватает на заряд конденсатора? Ведь суть одна и та же, просто каналы мультиплексируются аппаратно. Я бы на месте производителя лучше вообще убрал этот одиночный режим, а в режиме сканирования добавил возможность самому создавать набор нужных каналов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Сегодня осциллографом проверил пин, на котором генерируется ШИМ и который является одним из входов AIN. АЦП в сканирующем режиме вносит не хилые помехи в ШИМ-сигнал, о чем собственно и писали в документации, что нельзя AIN использовать в качестве выходов :wacko:

Долбаный stm8 :wall:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
shematehnik    1

Делюсь решением проблемы, может найдется еще такой бедолага, у которого возникнет такая же проблема :D Наконец-то нашел причину. Все просто до смеха.

Попробовал читать регистры ADC_DRH и ADC_DRL в обратной последовательности и все стало работать правильно без влияния каналов друг на друга.

Видимо чтение регистра ADC_DRH запускает следующее преобразования, а тем временем я еще забираю оставшуюся часть значения из ADC_DRL.

Пришел к такому выводу чисто в отладке. В IAR есть такое окно Live Watch, где можно почти в реальном времени видеть состояние любого регистра или переменной.

Так вот настроил на одном входе 0 В, а на другом 3.3В. По логике должны быть поочередно в регистрах ADC_DRH и ADC_DRL значения 0x00:0x00 и 0x03:0xff соответственно или близкие к ним.

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

Потом вычитал два регистра как один 16-битный (в IAR не было такого дефайна): adc_ain6 = *(unsigned int*)&ADC_DRH

Значения стали записываться правильные. Потом попробовал наоборот читать ADC_DRL сразу, потом ADC_DRH и все также хорошо работало.

Странно, почему же в мануале написали порядок чтения наоборот...

Кстати, возникла мысль, что после чтения регистра ADC_DRH у меня возникает следующее прерывания до того, как я прочитаю ADC_DRL . Но в отладке такого замечено не было. До сих пор не совсем точно понимаю почему напряжения смешивались..

Изменено пользователем shematehnik

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Viktor26    300

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Вот ещё обратите внимание на работу такой конструкции

adc_ain5 = ADC_DRH << 8;

Процессор 8-ми битный, и регистр ADC_DRH тоже 8-ми битный. Поэтому сдвиг влево на 8 полностью обнуляет результат, потому что нет места для выдвигаемый битов. В результате adc_ain5 = 0 после такого сдвига.

А если переменная adc_ain5 типа unsigned int, то по уму надо делать так

adc_ain5 = ADC_DRH;
adc_ain5 <<= 8;

Вот теперь старшие биты благополучно сдвинутся и не пропадут.

Или сделать явное преобразование типа

adc_ain5 = (unsigned int)ADC_DRH << 8;

Изменено пользователем Yurkin2015

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас