Термометр на STM8L-Discovery

С полгода назад я приобрел платку STM8L-Discovery. К сожалению, на нашем сайте я не нашел простых схем для новичков. Сейчас я сам уже подразобрался во всём и хочу предложить вам интересную, на мой взгляд, поделку. Хочу сказать сразу, что схема может быть перенесена и на другой контроллер (плата ведь у нас отладочная, не охота ее под один только девайс использовать). Для этой цели мы можем использовать отладачик нашей платы, далее я опишу процесс переноса прошивки. Для тех, кто только начинает работать с STM, хочу сказать — все довольно просто. Для работы использую среду IAR embedded workbench.

Для начала разберемся с датчиком температуры. LM35 — аналоговый датчик, напряжение на выходе которого при 0 по Цельсию равно нулю, а далее возрастает на 10 мВ на градус. То есть напряжение в мВ делим на 10 и получаем температуру датчика.

Теперь разберемся, что такое динамическая индикация. Задача этого метода сократить количество проводов  при работе с несколькими разрядами. Идея заключается в следующем: от каждого разряда выводы соответствующих сегментов собираются вместе, а общие от каждого разряда подключаются отдельно. Далее зажигание происходит следующим образом: подключаются нужные сегменты + общий вывод нужного разряда. И так аналогично с каждым разрядом в цикле. При достаточно быстром переключении глаз не замечает мерцания и мы видим просто многоразрядное число.

Теперь переходим к самому интересному — к контроллеру. На нашей плате стоит МК STM8L152C6T6. Для работы устройства нам потребуется использование таймера и аналогово-цифрового преобразователя (АЦП).

Итак,  таймер нам необходим, чтобы переключать зажигаемые разряды. У нашего МК есть несколько таймеров, но я использую таймер 2 для простоты. Для подключения индикатора я использую порт В под сегменты и выводы 2 и 3 порта D для управления разрядами. Выбор основан на том, что выводы выведены на плате рядом друг с другом. Привожу код инициализации для индикации.

 static int ind[2] = { 0xFF,    0xFF,  };  char seg = 0;   /*переключение разрядов и сегментов по таймеру*/    #pragma vector=TIM2_OVR_UIF_vector  __interrupt void timer2_interrupt(void)  {  if( !seg )  {    PB_ODR = ind[0];    PD_ODR_bit.ODR2 = 0;    PD_ODR_bit.ODR3 = 1;    seg = 1;  }  else  {    PB_ODR = ind[1];    PD_ODR_bit.ODR2 = 1;    PD_ODR_bit.ODR3 = 0;    seg = 0;  }  TIM2_SR1_bit.UIF = 0; }   void Init( char priority ) {   PB_DDR = 0xFF;   PB_CR1 = 0xFF;      PD_DDR_bit.DDR2 = 1;   PD_DDR_bit.DDR3 = 1;   PD_CR1_bit.C12 = 1;   PD_CR1_bit.C13 = 1;       CLK_PCKENR1_bit.PCKEN10 = 1; //тактирование подано   TIM2_PSCR = 1;//предделитель частоты   TIM2_ARRH = 0x07;    TIM2_ARRL = 0x07;//уровень переполнения   TIM2_CR1_bit.URS = 1;    TIM2_EGR_bit.UG = 1;     TIM2_IER_bit.UIE = 1;    ITC_SPR5_bit.VECT19SPR = priority;   asm("RIM");//глобально разрешим прерывания   TIM2_CR1_bit.CEN = 1;// запустим таймер } 

Далее логично показать код самой индикации.

 /*код цифр*/ /*т. к. у меня индикатор с общим анодом, то для зажигания на общий вывод подаю высокий*/ /*потенциал, на сегмент низкий*/ /*каждому горящему сегменту соответствует бит 0, негорящему 1*/ /*биты идут от младшего к старшему в соответствии с сегментами A-E + точка*/ int map[11] =  {   0xC0, //0   0xF9, //1   0xA4, //2   0xB0, //3   0x99, //4   0x92, //5   0x82, //6   0xD8, //7   0x80, //8   0x90, //9   0x00 // error };  /*пишем цифру в нужный разряд*/ void wrDig( int digit, char pos, bool dp ) {   ind[0] = 0xFF;   ind[1] = 0xFF;   ind[pos] = map[digit];   if ( dp )     ind[pos] &= 0x7F;        }  /*пишем число из двух данных цифр*/ void wrNum( int d1, int d0 ) {   ind[0] = map[d0];   if (( d0 > 9 )&&( d0 < 0 ))     ind[0] = map[10];   ind[1] = map[d1];   if (( d1 > 9 )&&( d1 < 0 ))     ind[1] = map[10]; }  /*пишем целое число*/ void wrInt( long int  n ) {   int a, b;   if ( n > 99 )      n = n % 100;   a = n % 10;   b = (n-a)/10;   wrNum(b,a); } 

В библиотеке есть еще функции, но они е используются в проекте и их я не описываю тут; думаю, из их названия понятно их назначение.

Теперь разберемся с АЦП. Именно он преобразует напряжение на выходе датчика в понятный контроллеру цифровой код. Важным параметром является разрешение АЦП — размер результата в битах. Я выбираю 12 бит. Это значит, что при напряжении на входе АЦП, равном опорному ( Vref ) будет результат из 12 двоичных единиц 0x0FFF в шестнадцатиричной системе или 4095 в десятичной.

Код инициализации АЦП

   CLK_PCKENR2_bit.PCKEN20 = 1; //тактирование   ADC1_CR1_bit.ADON = 1; //включим преобразователь   ADC1_SQR1_bit.DMAOFF = 1; //вырубим контроллер DMA   ADC1_SQR4_bit.CHSEL_S2 = 1; //второй канал - пин 4 порта А   ADC1_TRIGR4_bit.TRIG2 = 1;   ADC1_CR2_bit.SMTP1=7;   ADC1_TRIGR1_bit.TSON = 1; //включим внутренний датчик температуры

Теперь завершающий штрих — все выше написанное связать воедино.

   float value;   int middle_value;   int res[BUF_SIZE];     /* настройка пинов для светодиода и кнопки*/ /*кнопка на пине С1*/   PC_DDR_bit.DDR1 = 0; //на вход   PE_DDR_bit.DDR7 = 1;//push-pull режим   PC_CR1_bit.C11 = 1;//разрешить прерывания пином /*зеленый светодиод на пине Е7*/   PE_CR1_bit.C17 = 1;//на выход   PC_CR2_bit.C21 = 1;   PE_ODR_bit.ODR7 = 1;//подать на выход 1   EXTI_CR1_bit.P1IS = 2;//прерывание от кнопки по спаду   asm( "RIM" );   

 

В итоге имеем следующее устройство.

Схема термометра на STM8L-Discovery

Схема для тех, кто будет делать на отдельной плате. Распиновка контроллера STM8L152C6T6

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

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

Как я уже говорил, можно перенести прошивку на внешний контроллер. Для этого к чипу подключаем питание и делаем выводы от пинов RESET и SWIM. В проекте не забываем поменять настройки и заголовочные файлы, если используем не STM8L52C6. Далее справа над LCD снимаем 2 джампера. Слева видим штыревые контакты, 4 штуки. К2 втором сверху подключаем вывод SWIM, к четвертому RESET от внешнего кристалла. И шьем.

Мой профиль на форуме