|
|
  |
Измерение напряжения с помощью АЦП, 16 битное SD16_A |
|
|
|
Apr 9 2012, 10:58
|
Участник

Группа: Участник
Сообщений: 24
Регистрация: 5-12-11
Пользователь №: 68 682

|
Добрый день. Хочу поставить индикатор на регулятор мощности, для этого я хочу дополнить регулятор MSP430F2013 на аналоговые входы которого я буду подавать уменьшенный сигнал и измеряя напряжение с помощью АЦП выводить его значение в процентах. Для этого сделал для отлатки устройство с регулируемым напряжением до 0.59 вольт . Как я понял для написания пограммы надо сделать таблицу соответствия напряжения его цифрвому коду в АЦП . Вот моя программа для этой цели где результат преобразования напряжения хранится в ChA0results: #include <msp430x20x3.h>
/* Arrays to store SD16 conversion results */ /* NOTE: arrays need to be global to */ /* prevent removal by compiler */ static unsigned int ChA0results = 0x00;
static unsigned int ch_counter=0; void main(void) { volatile unsigned int i; // Use volatile to prevent removal // by compiler optimization WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer BCSCTL2 |= DIVS_3; // SMCLK/8 SD16CTL = SD16REFON + SD16SSEL_1; // 1.2V ref, SMCLK SD16INCTL0 = SD16INCH0; // Set channel A0+/- SD16CCTL0 |= SD16SNGL + SD16UNI + SD16IE; // Single conv, 256OSR, unipolar, // enable interrupt SD16INCTL0 |= SD16INTDLY_0; // Interrupt on 4th sample SD16AE = SD16AE0; // P1.0 A0+, A0- = VSS for (i = 0; i < 0x3600; i++); // Delay for 1.2V ref startup while(1) { SD16CCTL0 |= SD16SC; // Set bit to start conversion _BIS_SR(LPM0_bits + GIE); // Enter LPM0 } }
#pragma vector = SD16_VECTOR __interrupt void SD16ISR(void) { switch (SD16IV) { case 2: // SD16MEM Overflow break; case 4: // SD16MEM0 IFG switch(ch_counter) { case 0: ChA0results = SD16MEM0; // Save CH0 results (clears IFG) SD16AE &= ~SD16AE0; // Disable external input A0+, A0 SD16INCTL0 &= ~SD16INCH_0; // Disable channel A0+/- ch_counter++; SD16AE = SD16AE0; // Reset external input to A0+/- SD16INCTL0 = SD16INCH_0; // Reset channel observed break; } _BIC_SR_IRQ(LPM0_bits); // Exit LPM0 } } Но у меня проблема с недопонимание работы этого АЦП. Я подаю напряжение на канал A0 и вижу результат на SD16MEM0 он его благополучно заносит в ChA0results. Но эти значени при каждом перезапуске программы разные. Подскажите пожалуйста как решить эту проблему и составить таблицу:для 0.59В-"результат преобразования" , 0.531В- , 0.472В- , 0.413В- , 0.354В- , 0.295В- , 0.236В- , 0.177В- , 0.118В- , 0.059В.
|
|
|
|
|
Apr 9 2012, 13:38
|
Участник

Группа: Участник
Сообщений: 24
Регистрация: 5-12-11
Пользователь №: 68 682

|
Цитата(MrYuran @ Apr 9 2012, 15:56)  А зачем таблицу? Намного проще тупо пересчитывать. Одно действие - умножение кода на коэффициент пересчета А как или что вы имеете в виду под словом пересчитать? я ведь не знаю какой код будет на выходе АЦП при подаче того или иного значения напряжения. Ну а таблицу я не имею ввиду как таковую, я просто хочу знать какой код будет соответствовать тому или иному напряжению.
|
|
|
|
|
Apr 9 2012, 14:07
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(chainikru @ Apr 9 2012, 17:38)  А как или что вы имеете в виду под словом пересчитать? я ведь не знаю какой код будет на выходе АЦП при подаче того или иного значения напряжения. А зачем его знать - пусть машина думает. Полная шкала АЦП соответствует напряжению опоры. То есть, если опора, скажем, Vref=1.5В, то Vin = Vref * (Code/0xFFFF) Ну, ещё могут быть нюанс в зависимости от режима АЦП (дополнительные коэффициенты, смещение шкалы, изменение разрядности итд.)
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Apr 9 2012, 14:38
|
Участник

Группа: Участник
Сообщений: 24
Регистрация: 5-12-11
Пользователь №: 68 682

|
Цитата(MrYuran @ Apr 9 2012, 18:07)  А зачем его знать - пусть машина думает. Полная шкала АЦП соответствует напряжению опоры. То есть, если опора, скажем, Vref=1.5В, то Vin = Vref * (Code/0xFFFF) Ну, ещё могут быть нюанс в зависимости от режима АЦП (дополнительные коэффициенты, смещение шкалы, изменение разрядности итд.) А под значение Code что взять? Ну а хочу я это использовать примерно так: #pragma vector = SD16_VECTOR __interrupt void SD16ISR(void) { if (SD16MEM0 < 0x3FFF) // SD16MEM0 > 0.3V?, clears IFG P1OUT &= ~0x01; else P1OUT |= 0x01; } но при нужных значениях будет гореть не 1 светодиод, а соответствующее значение на индикаторе
Сообщение отредактировал chainikru - Apr 9 2012, 14:41
|
|
|
|
|
Apr 9 2012, 20:45
|
Участник

Группа: Участник
Сообщений: 24
Регистрация: 24-01-12
Пользователь №: 69 858

|
Mr.Yuran верно сказал !!! Vin = Vref * (Code/0xFFFF) добавлю! Vin = Vref * (Code/0xFFFF)*k (k - коэфициент деления вашего делителя) Тоесть напряжение необходимо подвать через делитель напряжения (на двух резисторах) коэфициент выбираеться так, что-бы при макисмальном измеряемом напряжении напряжение на соответств входе АЦП не превышал опорное напряжение (и категорически не более напряжения питания контроллера). Рекомендую поставить стабилитрон между 0V и входом АЦП (внимание на токи низковольтных стабилитронов), есть так-же вриант с защитными диодами... Дальше... какая точность измерения нужна??? Встроенные АЦП чесно говоря какашечные(не у всех моделей MSP... ставьте акцент на сигма-дельта АЦП)... долбит как минимум 2-3 младших разряда (зависит от схемотехники) Варианты решения: 1: Использование фильтра ( в большинстве случаев достаточно среднего значения 10-15 замеров или классическая RC цепочка). 2: Использование таблицы (массива значений) соответствующей требуемой точности измерения (пример: значения от 0x00 до 0x05 соответствуют 0.1В, от 0x06 до 0x0B соответств 0,2В и.т.д.) 3: Использование внешних прецезионных АЦП (фильтр всё равно нужен). Советы: 1. Если производите измерение на индуктивной нагрузке уделите этому соответствующие меры защиты !!!!! (сапрессоры или пассивный/активный баласт... всё зависит от мощности нагрузки). 2. АЦП заводите в режим n-канального циклического измерения (замер получаем по прерыванию, вычисление фильтра и индикацию в теле main{}). 3. Не забываем о конденсаторах в непосредственной близости к контроллеру (цепи АVcc, Ref+,Ref-) 4. Вместо фильтра иногда! можно применить сдвиг (>>x) вправо. Каждый сдвиг уменьшает разрядность АЦП на x. (для уменьшения колбасни младших разрядов) 5. Для измерения слабых сигналов используем инструментальный операционный усилитель.... (кстати ОУ в любом случае лучше использовать пусть накрайняк ОУ сгорит, аля контроллер дороже.... хотя ОУ и контроллеры бывают разные  ) Всё пифо кончилось..... ушел спать....
|
|
|
|
|
Apr 9 2012, 21:28
|
Участник

Группа: Участник
Сообщений: 24
Регистрация: 5-12-11
Пользователь №: 68 682

|
Цитата(hash20 @ Apr 10 2012, 00:45)  Mr.Yuran верно сказал !!! Vin = Vref * (Code/0xFFFF) добавлю! Vin = Vref * (Code/0xFFFF)*k (k - коэфициент деления вашего делителя) 2: Использование таблицы (массива значений) соответствующей требуемой точности измерения (пример: значения от 0x00 до 0x05 соответствуют 0.1В, от 0x06 до 0x0B соответств 0,2В и.т.д.) Вот я и хочу использовать массив значений, но я думал что для каждого напряжения есть только одно значение. Для начала хочу написать программу для измерения постоянного напряжения (питание подаю с обычной батареи через резистры и меняю его значение переменным резистром) чтобы потм переделать под переменное. И вот для этого мне надо знать значения соответствующие 0.59В- , 0.531В- , 0.472В- , 0.413В- , 0.354В- , 0.295В- , 0.236В- , 0.177В- , 0.118В- , 0.059В на SD16MEM0, чтоб выводить на сколько процентов работает прибор. Но когда я начинаю измерять у меня значения на SD16MEM0 всегда получаются разные это нормально? А по формуле я не уверен что все понял. Что вы имеете ввиду под словом Code хотя я предполагаю что Code это код получаемый на SD16MEM0 Vref- опорное напряжение Vin-напряжение код которого я хочу получть. Правильно ли я вас понял? И может ли к примеру 0.413В соответствовать несколько значений на SD16MEM0, если да сколько измерений посоветуете, и в программе для одного напряжения задавать среднее значение или сразу группу значений?
Сообщение отредактировал chainikru - Apr 9 2012, 21:29
|
|
|
|
|
Apr 10 2012, 04:53
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(chainikru @ Apr 10 2012, 01:28)  Но когда я начинаю измерять у меня значения на SD16MEM0 всегда получаются разные это нормально? Нормально. Я, кажется, понял. Вам надо просто несколько фиксированных уровней? Так это ещё проще. Забиваете формулу в макрос, и даже считать ничего не придется в рантайме. Константы сосчитаются на этапе компиляции. Что-то типа: Код #define V_REF 1500ul // опора в мВ #define MAX_SCALE 0xffff // вся шкала АЦП #define V_1 590 // V1 в мВ #define V_2 531 // V2 в мВ #define V_3 472 // V3 в мВ ... #define CODE(v) (V_REF * v / MAX_SCALE) // макрос пересчета мВ в код АЦП
...
if (SD16MEM0 < CODE(V_1)) { } Чтобы меньше дрожало, можно усреднять по нескольким отсчетам, тем более что АЦП в МСП-шках поддерживает множественный запуск. То есть, за один запуск можно получить подряд несколько отсчетов, а потом их усреднить.
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Apr 10 2012, 09:33
|
Участник

Группа: Участник
Сообщений: 24
Регистрация: 5-12-11
Пользователь №: 68 682

|
Цитата(MrYuran @ Apr 10 2012, 08:53)  Нормально. Я, кажется, понял. Вам надо просто несколько фиксированных уровней? Так это ещё проще. Забиваете формулу в макрос, и даже считать ничего не придется в рантайме. Константы сосчитаются на этапе компиляции. Вы имеете ввиду написать чтото наподобие этого (но так что то не хочет работать) ? Код #include <msp430x20x3.h>
#define V_REF 1200ul // опора в мВ #define MAX_SCALE 0xffff // вся шкала АЦП #define V_10 590 // V10 в мВ #define V_9 531 // V9 в мВ #define V_8 472 // V8 в мВ #define V_7 413 // V7 в мВ #define V_6 354 // V6 в мВ #define V_5 295 // V5 в мВ #define V_4 236 // V4 в мВ #define V_3 177 // V3 в мВ #define V_2 118 // V2 в мВ #define V_1 59 // V1 в мВ #define V_0 0 // V0 в мВ #define CODE(v) (V_REF * v / MAX_SCALE)// макросы пересчета мВ в код АЦП
void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P1DIR |= 0x01; // Set P1.0 to output direction SD16CTL = SD16REFON + SD16SSEL_1; // 1.2V ref, SMCLK SD16INCTL0 = SD16INCH_1; // A1+/- SD16CCTL0 = SD16UNI + SD16IE; // 256OSR, unipolar, interrupt enable SD16AE = SD16AE2; // P1.1 A1+, A1- = VSS SD16CCTL0 |= SD16SC; // Set bit to start conversion
_BIS_SR(LPM0_bits + GIE); }
#pragma vector = SD16_VECTOR __interrupt void SD16ISR(void) { if (SD16MEM0 < CODE(V_6)) P1OUT &= ~0x01; else P1OUT |= 0x01; /* P1OUT|= 0x08 + 0x10+0x40 + 0x80; P2OUT|= 0x80; if (CODE(V_8)< SD16MEM0 < CODE(V_9)) P1OUT|= 0x04 + 0x08 + 0x10 + 0x20 + 0x80; P2OUT|= 0x80; if (CODE(V_7)< SD16MEM0 < CODE(V_8)) P1OUT|= 0x04 + 0x08 + 0x10 + 0x20 + 0x40 + 0x80;; P2OUT|= 0x80; if (CODE(V_6)< SD16MEM0 < CODE(V_7)) P1OUT|= 0x04 + 0x08 + 0x10 + 0x80; if (CODE(V_5)< SD16MEM0 < CODE(V_6)) P1OUT|= 0x04 + 0x10 + 0x20 + 0x40 + 0x80; P2OUT|= 0x80; if (CODE(V_4)< SD16MEM0 < CODE(V_5)) P1OUT|= 0x04 + 0x10 + 0x20 + 0x80; P2OUT|= 0x80; if (CODE(V_3)< SD16MEM0 < CODE(V_4)) P1OUT|= 0x08 + 0x10+0x80; P2OUT|= 0x80; if (CODE(V_2)< SD16MEM0 < CODE(V_3)) P1OUT|= 0x04 + 0x08 + 0x10 + 0x20; P2OUT|= 0x80; if (CODE(V_1)< SD16MEM0 < CODE(V_2)) P1OUT|= 0x04 + 0x08 + 0x20 + 0x40; P2OUT|= 0x80; if (CODE(V_0)< SD16MEM0 < CODE(V_1)) P1OUT|= 0x08 + 0x10; if (SD16MEM0 < CODE(V_0)) P1OUT|= 0x04 + 0x08 + 0x10 + 0x20 + 0x40 + 0x80; P2OUT|= 0x80; */ _BIC_SR_IRQ(LPM0_bits); // Exit LPM0 } } в if (SD16MEM0 < CODE(V_6)) P1OUT &= ~0x01; else P1OUT |= 0x01; светодиод горит даже при отсутствии напряжения хотя елси вместо CODE(V_6) написать 0x7FFF //SD16MEM0 > 0.3V?, clears IFG то покра немерее яркость светодида меняется при изменении напряжения (почти выключается)
Сообщение отредактировал chainikru - Apr 10 2012, 10:07
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|