Хинт: Замер напряжения питания на STM8L

Иногда бывает полезно узнать напряжение питания МК. Причем не через PVD, который просто покажет, что оно в одном из нескольких промежутков, а точнее. Зачем? Ну например для налюдения за разрядом батареи. Или для корректировки значений, замеряемых АЦП (в камнях 32 выводами нету пина Vref, и единственный доступный ИОН — напряжение питания).

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

Несмотря на то, что единственный источник опорного напряжения для АЦП — питание, в STM8 все-же есть свой внутренний ИОН. Его напряжение около 1.225V (минимальное — 1.202, максимальное — 1.242, у меня было 1.220V). К нему подключен один из каналов АЦП, а значит, его можно измерить в любой удобный момент. Измеряем известное напряжение ИОНа относительно неизвестной опоры — питания.

Точное значение напряжения внутреннего ИОН было измерено для каждого МК еще на заводе и записано в специальный регистр в памяти. Имя ему — VREFINT_Factory_CONV, а адрес (ибо библиотека про этот регистр не знает) — 0x4910. А раз библиотека не знает, то прописывать его придется самому:
__near __no_init const unsigned char Factory_VREFINT @ 0x4910;

Теперь можем обращаться к регистру Factory_VREFINT. Но там хранится только младший байт от 12 битного результата преобразования. Старший байт всегда равер 0x6.

Напряжение внутреннего ИОНа измерялось при напряжении питания (читай — опорном напряжении АЦП) = 3V ±10mV.

Вот теперь, имея известное напряжение ИОНа при известном опорном, и только-что измеренное при неизвестном опорном напряжении, легко найти напряжение питание:

Я написал функцию, которая возвращает значение напряжения питания в виде unsigned int в «сантивольтах» :) То есть значение 330 соответствует 3.3V и т.д. Очень удобно для вывода на дисплей.

uint16_t GetVcc(void)
{
 uint32_t tmp_value;
 uint32_t res;
 uint32_t factory_ref_voltage; 
 uint8_t count;

 ADC_ChannelCmd(ADC1, ADC_Channel_Vrefint, ENABLE);
  
  tmp_value = 0;
  for (count=0; count<16; count++)
  {
    ADC_SoftwareStartConv(ADC1);
    while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
    tmp_value += ADC_GetConversionValue(ADC1);
    if (count != 0) tmp_value = tmp_value >> 1;
  };  

  if (Factory_VREFINT != 0)
   factory_ref_voltage = 0x600+Factory_VREFINT;
  else
   factory_ref_voltage = 1671;
  
  res = (factory_ref_voltage*100*3)/tmp_value;
    
  ADC_ChannelCmd(ADC1, ADC_Channel_Vrefint, DISABLE);
  
  return (uint16_t)res;  
};  


Вот эта хрень
if (Factory_VREFINT != 0)
   factory_ref_voltage = 0x600+Factory_VREFINT;
  else
   factory_ref_voltage = 1671;

Есть следствие того, что я столкнулся с жестокой дествительностью — в камень, который я использовал, «забыли» положить нужное значение. По адресу 0x4910 читаются одни нули. На случай повторения такого безобразия, в переменную заносится число, соответствующее среднему значению Vref — 1.225V.

Для корректной работы функции АЦП должен быть включен (тактирование и бит разрешения работы) и настроен на одиночное преобразование. Так-же, генератор опорного напряжения должен быть включен. Я не сделал этого в самой функции, так-как после запуска генератора требуется несколько миллисекунд на выход в рабочий режим. Кстати, если заботитесь о потреблении — оставлять генератор включенным не стоит: он жрет до 25мкА.

Вот один из вариантов кода настройки:
CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, ENABLE); 
ADC_Init(ADC1, ADC_ConversionMode_Single, ADC_Resolution_12Bit,ADC_Prescaler_2);
ADC_VrefintCmd(ENABLE);
ADC_Cmd(ADC1, ENABLE);

Теперь, зная напряжение питания, мы можем корректировать остальные измерения, которые делаются относительно него:

Code это то, что мы только-что измерили
n — 2^разрядность. Т.е. 4096 для 12бит, 1024 для 10 и т.д.

Иногда может понадобиться вывести опорное напряжение на пин МК. Например для того, чтоб утащить дальше по плате, или просто измерить. Это реализуется вот таким кодом:
CLK_PeripheralClockConfig(CLK_Peripheral_COMP, ENABLE);
COMP->CSR3 |= COMP_CSR3_VREFOUTEN;
RI->IOSR2 |= RI_IOSR2_CH8E;

Во-первых, подается тактирование на компаратор — это нужно для работы Routing Interface. Затем, включается выходной буфер для ИОН. И наконец, замыкается один из ключей, подключающих этот буфер к внешнему пину. В данном случае это будет PD7. Если D7 занят — нет проблем! Можно подключить ИОН к PD6
RI->IOSR3 |= RI_IOSR2_CH9E;

или PC2
RI->IOSR1 |= RI_IOSR2_CH7E;
  • +7
  • 30 июля 2012, 23:52
  • dcoder

Комментарии (62)

RSS свернуть / развернуть
Точное значение напряжения питания было измерено для каждого МК еще на заводе

Тут не понял маленько. Может значение опорного напряжения?
Тоже была как то такая мысля корректировать значение АЦП. Только на AVR. Даже прикинул расчет. Потом подумал, что опорное напряжение все равно плавает и без подобного зашитого значения VREFINT_Factory_CONV точно корректировку сделать не получится. Кстати, кто знает, есть ли на STM32 такое значение? Даташит еще не весь скурил) Да и больше миландровский контроллер щас мучаю.
0
Спасибо, исправил :)

Насчет стм32 не знаю, но готов спорить, что есть. У них вообще периферия довольно похожа.
0
Вообще несколько странновато, иметь внутренний опорник и не иметь возможности опереть на него АЦП.
0
  • avatar
  • Vga
  • 31 июля 2012, 00:23
Согласен, но что поделаешь — приходится изворачиваться :) Хотя, можно вывести внутреннюю опору на ножку, а эту ножку замкнуть на вход Vref, если он есть.
0
Это тоже как-то через жопу. Лучше что-нить в духе REG_ADC_VREF = USE_INT_VREF. Вот и возникает вопрос — почему так странно сделано?
0
Вот был же семинар по стм8 — можно было до их инженеров (хотя конечно не STM) доебаться :))
+1
Еще б я на нем был)
0
Это нормальная практика. Так можно с экономить энергию при постоянном мониторенге питания. В таком случае не требуется делитель.

Ti пошли по другому пути и получился весьма печальный исход. Там при замере питания ацп жрет более 1ма.
0
Тут ацп при замере (на время выборки+преобразования) тоже жрет 1мА.

А для постоянного мониторинга (типа — ой, разряжаемся) — лучше PVD
0
У Ti на линии питания постоянно весит делитель. Который там явно не к месту. Он отжирает ~200uA постоянно. Его можно вырубить только перейдя в глубокий сон.
0
Хы действительно, хрень какая-то
0
Это где? В ориентированных на микропотребление MSP430? Что-то не верится. Может, ты просто не так понял?
0
CCxxxx. Там именно так. И там таких радостей чуть больше чем дохрена. Там вообще управление потреблением энергии сделано через анус. В stm8 намного лучше продуман этот вопрос.
0
А я тут кстати немного игрался с ланчпадом — как же там все просто и красиво сделано (430g2553) :))
0
А, ну это во первых не микропотребляющие (вроде), а во вторых — не TI-шная разработка.
0
Серия ССxxxx расчитанна на работу от coin battery. Они все умеют от нее работать. Им достаточно одной CR2032 и в зависимости от активности могут от нее проработать более двух лет.
0
Возможно.
Ну тогда это косяком попахивает. Делитель можно было бы и отключаемым сделать.
0
Делитель выключается только вместе с BOD. Но BOD выключается только во время глубокого сна.
0
Ага, а они (TI) еще кричали что мол наш bod работает всегда, так-как не жрет ток! Совсем :)
0
Всегда он точно не работает. В даташите об этом прямо сказано. И жрет он 200uA об этом тоже в даташите написано.
0
Ну может это для СС. Где-то я точно читал про то, что не жрет тока -> можно не выключать
0
«Можно не выключать» в Ti поняли слишком буквально. В ССxxxx лишнюю периферию выключить невозможно либо это делается через жопу. Это доставляет много удовольствия.
0
Периферия там работает всегда и жрет энергию тоже всегда.
0
Как то у меня резко пропало желание покупать те часики… :)
0
Конкурентов особо у них нет. По мимо недостатков у них много плюсов. Все очень хорошо документировано, удобная периферия, минимум внешних элементов. Очень высокая плотность кода. У меня здоровый проект влез в 4Кб. Хотя этот же проэк на авр еле залез в 13Кб. И еще полно мелких радостей.
0
А сами контроллеры где и за сколько достать можно? (понятно, что страны у нас разные, но хоть примерную стоимость интересно услышать). Я имею ввиду эти CC — хочу поиграть с МК со встроенным радио. Кстати, ВЧ часть там сильно сложная или не?
0
CC2540F128 — 5-6$
CC2510F32 — 5$
CC2530F256 — 4.3$

ВЧ в зависимости от того, что хочешь. Если свое лепить, то довольно просто но это только для проприетарных трансиверов. Для BLE и ZigBee все намного сложнее, по сложности чуть сложнее LUFA.
0
Можно брать у буржуев через фирму. На прямую продажи запрещены или через официальных дистрибьюторов. Все чипы Ti продает Сканти Рус. Работает по СНГ.
0
Это фишка MCS51 с его переменной длиной команд (1-3 байта, против 2=4 у AVR).
0
Хронос чтоль? В хроносе новые CC430, где выкинута процессорная часть оригинальных разработчиков и вместо нее засунут MSP430. Так что подобные косяки, вероятно, пофиксили.
0
Ага, ну в общем надо еще почитать про них. Главное что меня смущает — чтоб они не были слишком большие и толстые :)
0
Размерчик у них адовый, это да. Ну и если их реально носить — то скорее всего быстро придут в негодность — зацарапается стекло (оно там пластиковое) и развалится ремешок (он там нестандартный, а пластиковые ремешки долго не живут).
0
Пластиковые ремни говно, это да. Я на своих его заменил на синтетический (после того, как он порвался :)

Стекло можно пленкой заклеить
0
Я как раз пластиковые предпочитаю. Просто меняю их ежегодно. А что за синтетический?
Проблема кроноса в том, что там нестандартный ремешок, который фиг поменяешь.
0
0
Можно, но оно выпуклое и без рамки — пленка задиратсья с краю будет.
0
Ну это их. А CCxxxx — чье-то наследие. TI-шная разработка на нем — это CC430.
0
Ты их сравни. Сразу начнешь воротить нос от CC430. SoC c 51 ядром их превосходят.
0
У меня нету CC2xxx, только CC430. Так что лучше сам освети различия.
0
1)Цена.
2)У CC430 питание и частота зависят друг от друга.
3)Немного хуже энергопотребление. Но режим idle значительно превосходит SoC с 51м ядром.
4)Не раздельное тактирование таймеров и ядра/периферии.
5)Требуется немного больше внешних элементов.
6)Жесткая ограниченность в выборе.
0
Ты слишком лаконичен. Неясны пункты 2, 5, 6.
А вообще семейство новое (к кроносу вообще бумажка с предупреждением «experimental silicon, beware of bugs» приложена), такие моменты как разнообразие со временем утрясутся. Цены, надеюсь, тоже.
0
Разнообразие точно не увеличится. Совсем новые BLE чипы вышли без какого либо намека на 430. Тоже самое вышло и GPS.

2) У них точно такая же проблема как и AVR. А если учесть, что AES ускоритель работает, только на максимальной частоте ядра то и вовсе выходит просто песня))

5) Питание походу не совместимо с 430. Поэтому добавлены дополнительные пины на которые нужно вешать керамику.

6) CC430 ограничивается только проприетарным трансивером. Ни намека на BLE, ZigBee, GPS. А ведь скоро уже появится SoC с Wi-Fi.
0
2) Не понял, какая именно? То, что чем выше питание, тем выше допустимая частота работы?
6) А у CC25xx с этим лучше? ZigBee кстати вроде можно и на проприетарном реализовать?
0
2) Точно также как и на АВР. В даташите есть график. 20Mhz доступно только для 3.3V

6) ZigBee невозможно реализовать программно.
0
Интересное дело. И часто бывает, что «забывают»?
Сталкивался, когда в STM32 «клали» в два раза больше памяти, чем по маркировке на корпусе, такого же еще не видел. =)
0
Не знаю, этот камень вообще какой-то странный :) Он думает, что к нему подключен кварц, даже когда кварца нету (с печальным исходом, естественно)
0
какой камень?
0
STM8L152K6T6
0
У меня из таких только 151 серия, но в ней все нормально.
0
У меня еще десяток 152 лежит. Из них штуки 3 из той-же партии, что и этот. Потихоньку буду собирать статистику :)
0
в последней формуле n принимается равным 1024 для 10 бит и 4096 для 12.
+1
и результат будет всегда в минусе на цену одного дискрета, относительно реального значения?..
0
теорию аналого-цифрового преобразования нужно знать )
максимальный код всегда равен (N-1)/N от полной шкалы.
лучше всего это понять анализируя передаточную характеристику АЦП, т.е. зависимость выходного кода от входного напряжения.
вот первая ссылка из кугла
www.efo.ru/doc/Silabs/Silabs.pl?2089
+1
максимальный код равен
(N-1)/N ??
Я почему-то считал, что максимальный код, который может выдать преобразование равен (N-1), где N — разрядность АЦП.
0
ну вот я попробовал накидать как «искажается» передаточная если считать N-1
www.imageup.ru/img242/1005620/adc_error.jpg.html
+1
забыл указать, что пример для четырех битного преобразования
0
если так, то получается что при наличии, скажем, 8-битного АЦП и опоры в 2,56 В мы никогда не сможем померить напряжение в 2,56, потому что при 2,55 уже получаем максимально возможное значение…
вобщем-то да, вы правы
0
Да, так и есть максимальное значение выходного кода АЦП N-1, однако при переводе нельзя использовать N-1, а только N
+1
какое по-детски наивное заблуждение овладевало мною все это время. теперь стыдно смотреть в код устройств, давно находящих в серии… искренне каюсь…
0
Поправил, заодно еще дописал про вывод опорного напряжения на пин
0
Нельзя плюсовать, поэтому простое человеческое спасибо! Собрался сделать что-то такое, хорошо, что перед этим поискался. Очень пригодилось.
0
Приветствую. Спасибо за статью.

А в формуле не наоборот должно быть?
Ведь пропорция такая:
3v — Vref_fact
Vcc — Vref
Соответственно: Vcc = Vref * 3 / Vref_fact

В Stm32 (STM32F405xx, STM32F407xx) VREFIN_CAL лежит по адресу 0x1FFF 7A2A — 0x1FFF 7A2B и измерена при 3,3В, смотрите в даташите, в Reference manual этого нет.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.