Вольтметр/амперметр для лабораторного БП

В ходе размышлений над проектом лабораторного блока питания решил я немного поизучать возможности AVR в качестве измерителя напряжения и тока. Как известно, контроллеры серии ATMega имеют 10-разрядный АЦП, который можно использовать для измерения.
Вообще, точные измерения любых величин — дело далеко не простое. Начинается все с резисторов повышенной точности, за ними идут источники опорного напряжения, а там уже и до термокомпенсации недалеко… Ну и хорошо бы иметь заведомо точный измерительный прибор для калибровки нашего изделия.
Но на этом не заканчивается, в дело вступают проблемы программного характера: не всегда полученное количество отсчетов АЦП можно непосредственно вывести на экран, вступают в силу ошибки накопления, округления и прочие.
Насколько возможно, я пытался уйти от всех этих проблем и сохранить простоту схемного решения. Заодно в программировании поупражняться. Конечно, это только макет, но он определяет приоритеты в конструировании готового прибора.
Итак, что же мы имеем?

Примечание к схеме: на каждый вывод порта B нужно привесить резистор, у меня 680 Ом.
Прибор собран на контроллере ATMega8 в корпусе TQFP-32, это дает нам 2 дополнительных входа АЦП. Обвеса минимум, буквально один блокировочный конденсатор по питанию. Тактирование — внутренняя RC-цепочка на 8 Мгц.
Диапазон измерения напряжения, который я хотел получить — 30В, а тока — 5А. Для входа напряжения применил делитель на резисторах, для входа тока подключил напрямую, в готовой схеме напряжение с шунта будет усиливаться операционным усилителем с таким расчетом, что 5А будет соответствовать 5В на выход. Сейчас вход тока просто открыт, можно мерять напряжение до 5В.
Результаты измерений отображаются на семисегментных индикаторах — 3 разряда напряжения и 4 разряда тока. Совершенно случайно получилось, что зеленый индикатор с общим анодом, а красный — с общим катодом. Вначале я долго пытался приспособить какую-нибудь готовую универсальную библиотеку для семисегментников, потом пытался написать свою, тоже универсальную, в итоге, провозившись неделю и не приблизившись к цели ни на шаг, бросил и написал за два часа быдлокод, сразу все заработало.
Принцип в том, что гасятся все разряды выводом в порт не нулей или единиц, а маски, соответствующей подключению разрядов, включаются разряды инвертированием соответствующего бита, а сегменты в случае общего анода надо инвертировать. Это немного утяжелило восприятие кода, но работает.
Вторая задача, которую пришлось решать — источник опорного напряжения. AVR позволяет использовать 3 источника — напряжение питания, встроенный ИОН 2,56В или внешний AREF. Напряжение питания показалось мне недостаточно стабильным, а встроенный ИОН — маловатым, для измерения 30В будет слишком большой коэффициент деления. К тому же была информация о недостаточной точности этого источника.
Для упрощения расчетов я решил сделать простой ИОН на 5.12В на TL431.

Точное значение опорного напряжения устанавливается многооборотным подстроечником R2, делитель для измерения напряжения тоже дополнен подстроечником R6, потому что точных резисторов необходимого номинала у меня нет. Если у вас есть, вы можете пересчитать под другие номиналы. К тому же, в результате борьбы за точность я изменил максимальное входное напряжение до 32В, так что подстроить оказалось легче, чем перепаивать.
Теперь перейдем к программированию. Здесь есть две задачи: перейти от отсчетов АЦП к вольтам и повысить точность измерения.
После нескольких вариантов пересчета я остановился на следующем.
Задаемся максимальным выходным значением 30.0В, десятичную запятую выкидываем, получается 300. Записываем степени двойки до максимального значащего разряда нашего АЦП и смотрим, какие степени в сумме составляют наше число: 256+32+8+4==300. Теперь, чтобы получить весь диапазон, нужно сделать: value=(ADC>>1)+(ADC>>4)+(ADC>>6)+(ADC>>7);
Удобно для этого пользоваться каким-нибудь табличным редактором, у меня, например, OpenOffice Calc:

Вот тут-то и вылазят все ошибки точности и округления.
Для повышения точности хорошо бы применить программные методы, например оверсемплинг, потому что 10 бит недостаточно даже для одной цифры после запятой в диапазоне 0-30В.
Для того, чтобы поднять разрядность на N бит, нужно сделать 4^N измерений и затем сумму этих измерений поделить на 2^N, то есть сдвинуть вправо N раз.
По-хорошему, нужно добавить еще шум, но я пока решил обойтись без этого.
Код:
//ANTONLUBA 2013
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include "bits_macros.h"
uint8_t segment[] =
//pgfedcba
{
0b00111111, //0
0b00000110, //1
0b01011011, //2
0b01001111, //3
0b01100110, //4
0b01101101, //5
0b01111101, //6
0b00000111, //7
0b01111111, //8
0b01101111 //9
};
//Сегменты
#define PORT_IND PORTB
#define DDR_IND DDRB
//Цифры
#define PORT_DRV PORTD
#define DDR_DRV DDRD
//#define CREFERENCE (unsigned int) 5120
//#define VREFERENCE (unsigned int) 2400
//#define COMMA_POS0 1 //позиция точки вольтметра (сделана переменной)
#define COMMA_POS1 8 //позиция точки амперметра
uint8_t digits[10];// = {6,5,4,3,2,1,0); // буфер для разрядов
uint8_t current_digit, // текущая цифра
current_num, //индикатор
current_chan, //канал АЦП
comma_pos0; //позиция точки вольтметра
unsigned int adc_counter1, adc_counter2; //счетчики суммирований значений АЦП
unsigned long int value1, value2; //значения сумм АЦП
void BIN2BCD(uint8_t *buffer, unsigned int n)
{
//int n;
//n=number;
buffer[4]=0;
while(n>=10000) {buffer[4]++;n-=10000;}
buffer[3]=0;
while(n>=1000) {buffer[3]++;n-=1000;}
buffer[2]=0;
while(n>=100) {buffer[2]++;n-=100;}
buffer[1]=0;
while(n>=10) {buffer[1]++;n-=10;}
buffer[0]=n;
}
void BIN2BCDslow(uint8_t *buffer, int n)
{
uint8_t a0,a1,a2,a3, b0,b1,b2,b3,b4;
a0= (uint8_t)(n&0x0F);
a1= (uint8_t)((n&0xF0)>>4);
a2= (uint8_t)((n&0x0F00)>>8);
a3= (uint8_t)((n&0xF000)>>12);
b0=a0-4*(a3+a2+a1)-20;
b1=6*a2+2*a1-138;
b2=a3+2*a2-46;
b3=4*a3-64;
b4= 7;
while (b0>10)
{
b0+=10;
b1--;
}
while (b1>10)
{
b1+=10;
b2--;
}
while (b2>10)
{
b2+=10;
b3--;
}
while (b3>10)
{
b3+=10;
b4--;
}
buffer[0]=(uint8_t)b0;
buffer[1]=(uint8_t)b1;
buffer[2]=(uint8_t)b2;
buffer[3]=(uint8_t)b3;
buffer[4]=(uint8_t)b4;
}
int main (void)
{
//Инит портов
PORT_IND=0xFF;
DDR_IND=0xFF;
PORT_DRV=0x00;
DDR_DRV=0xFF;
//Запустить таймер
TCCR0 = (1<<CS01)|(1<<CS00);
TIMSK = (1<<TOIE0);
//Запустить ADC
ADMUX = (0<<REFS1)|(0<<REFS0)|(0<<ADLAR)|(0<<MUX3)|(1<<MUX2)|(1<<MUX1)|(0<<MUX0);
ADCSRA = (1<<ADEN)|(1<<ADSC)|(0<<ADFR)|(0<<ADIF)|(1<<ADIE)|(1<<ADPS2)|(0<<ADPS1)|(0<<ADPS0);
sei(); //вкл прерывания
while (1)
{
}
}
ISR(TIMER0_OVF_vect) //переполнение таймера
{
if (current_num==0) //индикатор 0 - вольтметр
{
//вывести цифру
PORT_DRV=0b01111000; //маска общий катод == 1
if ((current_digit>comma_pos0)&&(digits[current_digit]==0)
&&(digits[current_digit+1]==0))
{
PORT_IND=0xFF; //выключить цифры левее старшего 0
}
else
{
PORT_IND=~segment[digits[current_digit]]; //отобразить цифру
}
if (current_digit==comma_pos0) //если позиция точки
{
ClearBit(PORT_IND, 7); //вывести точку
}
SetBit(PORT_DRV, current_digit); //включить разряд
current_digit++; //увеличить номер текущего разряда
if (current_digit==3) //если вышли за пределы
{
current_digit=0; //обнулить номер разряда
current_num=1; //индикатор1
}
}
else
{
//вывести цифру
PORT_DRV=0b01111000; //маска общий катод == 1
if ((current_digit+5>COMMA_POS1)&&(digits[current_digit+5]==0)
&&(digits[current_digit+5+1]==0))
{
PORT_IND=0x00; //выключить цифры левее старшего 0
}
else
{
PORT_IND=segment[digits[current_digit+5]];//отобразить цифру
}
if (current_digit+5==COMMA_POS1) //если позиция точки
{
SetBit(PORT_IND, 7); //вывести точку
}
ClearBit(PORT_DRV,(current_digit+3));//включить разряд, 3 == ширина вольтметра
current_digit++; //увеличить номер текущего разряда
if (current_digit==4) //если вышли за пределы
{
current_digit=0; //обнулить номер разряда
current_num=0; //индикатор0
}
}
}
ISR(ADC_vect) //АЦП преобразование выполнено
{
unsigned long int temp;
if (current_chan==0)
{
//напряжение
if (adc_counter1<1024) //15 значащих разрядов в 20-битном числе
{ //пока не сложим 1024 раза
value1+=ADC; // складываем
adc_counter1++; //ув. счетчик
}
else //если достигли
{ //сдвигаем на 5 разрядов
value1=value1>>5; //15bit
// и приводим к нужному значению
temp =((value1>>4)+(value1>>6))>>3; //2560/8 = 320
comma_pos0 = 1;
if (temp<=99) //если меньше 10В
// приводим к другому значению
{temp = ((value1>>4)+(value1>>5)+(value1>>8)); //3200
comma_pos0 = 2; //и передвигаем точку
}
BIN2BCD(&*digits, (unsigned int)temp); //преобразуем в код
adc_counter1=0; // сбрасываем счетчик
value1=0; // и значение
}
SetBit(ADMUX,MUX0); //переключаем канал АЦП
current_chan=1; //и переменную
SetBit(ADCSRA, ADSC); //старт преобразования
}
else
{
//ток //здесь почти то же, только 16 значащих бит
if (adc_counter2<4096) //в 24-битном числе
{
value2+=ADC;
adc_counter2++;
}
else
{
value2=(value2>>6); //16 bit
temp = (((unsigned int)value2>>1)+((unsigned int) value2>>3))>>3;
BIN2BCD(&*digits+5, (unsigned int)temp);
adc_counter2=0;
value2=0;
}
ClearBit(ADMUX, MUX0);
current_chan=0;
SetBit(ADCSRA, ADSC);
}
}
Ну и напоследок, несколько фото:
Напряжение, больше 10В:

Напряжение, меньше 10В:

Ток:

Снизу:

В аттаче схема и модель для протеуса, исходник и готовая прошивка (подсунуть протеусу).
PS: кстати, в исходнике есть функция BCDSlow. Она, по-идее, должна быть BCDfast, но на малых значениях проигрывает обычному вычитанию. Я ее переводил с пиковского ассемблера, там она получается быстрая, по сути, все делается сдвигами и сложениями и в циклах получается не больше 9 итераций на каждый десятичный разряд, но на С что-то не заладилось. Можете глянуть на предмет оптимизации.
- +4
- 05 июня 2013, 18:42
- antonluba
- 2
Файлы в топике:
Model_sch.zip, Firmware.zip
Прибор собран на контроллере ATMega8 в корпусе TQFP-32, это дает нам 2 дополнительных входа АЦП.А смысл? Все равно ADC0-ADC5 не задействованы. Лучше уж с них сэмплировать, чтобы прошивка была совместима с DIP28-версией.
Для того, чтобы поднять разрядность на N бит, нужно сделать 4^N измерений и затем сумму этих измерений поделить на 2^NПо моему, нужно все же 2^N измерений.
А смысл? Все равно ADC0-ADC5 не задействованы.Подключится еще куча светодиодов и реле, скорее всего все будут заняты, а 6-7 канал только как вход АЦП можно использовать.
Если хочешь высокую точность АЦП — лучше к порту C ничего, кроме аналоговых (ну, еще, возможно, цифровых) входов не цеплять. Если GPIO не хватает — можно мультиплексировать. Еще можно 74HC595 поставить на сегменты или (пожалуй, этот вариант даже лучше) разряды индикатора.
Впрочем, если не ориентироваться на повторение схемы другими, предпочитающими DIP — то и ADC6/ADC7 ничем не хуже.
Впрочем, если не ориентироваться на повторение схемы другими, предпочитающими DIP — то и ADC6/ADC7 ничем не хуже.
потому что 10 бит недостаточно даже для одной цифры после запятой в диапазоне 0-30В.
Как так?
octave:1> 30/1024
ans = 0.029297
около 3 сотых вольта на один разряд ацп.
Пересчет можно и через умножение сделать, про быстродействие же речи не идет? Да и аппаратное умножение есть.
Попробовал. Сильно дрожит младший разряд, не остановишь. Кажется, это было одной из причин, почему я в оверсемплинг полез.
Ну и, кстати, если по нормальному, надо тогда BIN2BCD делать для типа float с полноценной десятичной точкой, я даже не представляю, как это.
я делал проще и с точкой или точками
kalobyte.com/7-seg-dinamicheskaya-indikaciya
kalobyte.com/7-seg-dinamicheskaya-indikaciya
- kalobyte-ya
- 05 июня 2013, 23:27
- ↑
- ↓
Ага, (digit|80). Там вообще много чего переделать надо. Под полноценной я имел в виду, чтобы функция преобразовывала тип float в набор десятичных цифр с точкой, что-то типа ftoa(), только в сегменты.
слишком много ресурсов жрать будет
поэтому я не стал заморачиваться так глубоко
поэтому я не стал заморачиваться так глубоко
- kalobyte-ya
- 06 июня 2013, 16:57
- ↑
- ↓
AVR121: Enhancing ADC resolution by
oversampling
На каждый дополнительный разряд АЦП надо сделать 4^n лишних семплов. После суммирования оверсемплов результат надо побитно сдвинуть вправо n раз (разделить на 2^n).
oversampling
For each additional bit of resolution, n, the signal must be oversampled four times.
Thescale factor, sf, given by Equation 3-2, is the factor, which the sum of 4n samples should be divided by, to scale the result properly. n is the desired number of extra bit.
Equation 3-2: sf = 2^n
На каждый дополнительный разряд АЦП надо сделать 4^n лишних семплов. После суммирования оверсемплов результат надо побитно сдвинуть вправо n раз (разделить на 2^n).
- me9atherion
- 05 июня 2013, 19:48
- ↓
Подробно не просматривал (времени нет), но первое, что бросилось в глаза — надо бы входы АЦП зашунтировать конденсаторами…
Ведь измерения проводятся только для индикации? Тогда нет смысла обновлять показания чаще нескольких раз в секунду.
Склеить бы ФНЧ с частотой среза ~10 Гц, хотя бы RC первого порядка, тогда и посмотреть, быть может и оверсэмплинг с дизерингом (тьфу ты, мерзопакость какая) не понадобятся.
Склеить бы ФНЧ с частотой среза ~10 Гц, хотя бы RC первого порядка, тогда и посмотреть, быть может и оверсэмплинг с дизерингом (тьфу ты, мерзопакость какая) не понадобятся.
Автор говорит, что хотя на схеме их нет — на макетке они есть.
Ведь измерения проводятся только для индикации? Тогда нет смысла обновлять показания чаще нескольких раз в секунду.Если ты о конденсаторах — то они необходимы для сглаживания бросков входного тока АЦП в моменты сэмплирования.
Имеется в виду входной ток зарядки конденсатора УВХ? По-хорошему, там должен быть свой входной буфер. Если УВХ встроено, то разработчик должен был буфер предусмотреть.
Но я не (с)только об этом, я говорил о кондиционировании именно сигнала.
Но я не (с)только об этом, я говорил о кондиционировании именно сигнала.
Буфера нет:
When the channel is selected, the source must drive the S/H capacitor through the series resistance (combined resistance in the input path).
The ADC is optimized for analog signals with an output impedance of approximately 10 k or less. If such a source is used, the sampling time will be negligible. <...> The user is recommended to only use low impedant sources with slowly varying signals, since this minimizes the required charge transfer to the S/H capacitor.
ATmega8_L_datasheet.pdf, p. 195.
When the channel is selected, the source must drive the S/H capacitor through the series resistance (combined resistance in the input path).
The ADC is optimized for analog signals with an output impedance of approximately 10 k or less. If such a source is used, the sampling time will be negligible. <...> The user is recommended to only use low impedant sources with slowly varying signals, since this minimizes the required charge transfer to the S/H capacitor.
ATmega8_L_datasheet.pdf, p. 195.
Вот и я о том. В кондиционировании сигнала тоже не так много толку — конечно, выше Fд/2 резать надо, но от шума АЦП это не спасет. Расширять диапазон АЦП для трех разрядов незачем (разве что если калибровка программная), но вот шум давить придется — без этого можно на AVR только на 8 разрядов рассчитывать.
Производитель декларирует:
• 10-bit Resolution;
• 0.5 LSB Integral Non-linearity;
• ±2 LSB Absolute Accuracy.
О шуме ничего не сказано. Какая конкретная информация есть у вас о шуме АЦП Atmega-8? Почему вы полагаете, что два разряда можно выбросить в мусорку? Это такой собственный шум?
На схеме я вижу входные для АЦП цепи как раз порядка 10 килоом, нормально, можно сделать меньше, сделать простейшую аналоговую фильтрацию. Было бы три полных десятичных разряда, как раз и понадобилось бы десять бит.
Да, у Atmega ещё есть такой интересный режим как ADC Noise Canceler.
• 10-bit Resolution;
• 0.5 LSB Integral Non-linearity;
• ±2 LSB Absolute Accuracy.
О шуме ничего не сказано. Какая конкретная информация есть у вас о шуме АЦП Atmega-8? Почему вы полагаете, что два разряда можно выбросить в мусорку? Это такой собственный шум?
На схеме я вижу входные для АЦП цепи как раз порядка 10 килоом, нормально, можно сделать меньше, сделать простейшую аналоговую фильтрацию. Было бы три полных десятичных разряда, как раз и понадобилось бы десять бит.
Да, у Atmega ещё есть такой интересный режим как ADC Noise Canceler.
Привет всем.
Интересный вольт-амперметр.
Может добавить режим ADC Noise Reduction?
Хорошо бы кнопку добавить для программной коррекции смещения нуля ОУ. Хороший результат у меня получался с AD8552.
Оба канала хорошо бы сделать двухдиапазонными Амперметр 0-999,9 ма, 1,000 — 9,999А, вольтметр 0- 9,999, 1,000 — 30,00.
Переключения каналов не сложно реализовать. В канале Амп. стоит 2 ОУ с разным усилением (AD0, AD1) Вольтметр — два делителя на (AD2,AD3). Логика проста: если значение AD1, например, меньше нижнего значения «Предел», то выводим AD0.
Могу привести код.
Интересный вольт-амперметр.
Может добавить режим ADC Noise Reduction?
Хорошо бы кнопку добавить для программной коррекции смещения нуля ОУ. Хороший результат у меня получался с AD8552.
Оба канала хорошо бы сделать двухдиапазонными Амперметр 0-999,9 ма, 1,000 — 9,999А, вольтметр 0- 9,999, 1,000 — 30,00.
Переключения каналов не сложно реализовать. В канале Амп. стоит 2 ОУ с разным усилением (AD0, AD1) Вольтметр — два делителя на (AD2,AD3). Логика проста: если значение AD1, например, меньше нижнего значения «Предел», то выводим AD0.
Могу привести код.
Я не такой профи, что бы суметь написать.
Но, из личного опыта построения и использования показометра знаю, что надо добавить, что бы получился «Народный Показометр».
Могу привести код (не моя разработка) двух-диапазонного амперметра + вольтметр на меге 8. Оттуда можно применить кусок кода выбора канала ADC.
У вас классно реализован оверсемплинг, 4 разряда,… добавить бы еще несколько плюшек.
Но, из личного опыта построения и использования показометра знаю, что надо добавить, что бы получился «Народный Показометр».
Могу привести код (не моя разработка) двух-диапазонного амперметра + вольтметр на меге 8. Оттуда можно применить кусок кода выбора канала ADC.
У вас классно реализован оверсемплинг, 4 разряда,… добавить бы еще несколько плюшек.
'* Filename: Вольтметр, 2х диапазонный ампертметр *
'* Revision: 5.1 *
'* Controller: ATMEGA8 *
'* Compiler: BASCOM-AVR 2.0.6.1 *
'* Author: MACTEPOK *
'*******************************************************************************
$regfile = «m8def.dat» 'определяем контроллер
$crystal = 8000000 'внутренний генератор
'$sim
$lib «mcsbyte.lbx» 'подключаем библиотеку функций
Config Pinc.0 = Input: Portc.0 = 1 'кнопка Выбор
Config Pinc.1 = Input: Portc.1 = 1 'кнопка Вверх
Config Pinc.2 = Input: Portc.2 = 1 'кнопка Вниз
Config Portd = Output: Config Portb = Output 'порты на выход к которым подключен индикатор
Load1 Alias Portb.3 'нагрузка №1
Load2 Alias Portb.4 'нагрузка №2
Vibor Alias Pinc.0 'кнопка Выбор
Up Alias Pinc.1 'кнопка Вверх
Down Alias Pinc.2 'кнопка Вниз
Config Adc = Single, Prescaler = Auto, Reference = Avcc 'настраиваем АЦП
Dim W As Byte, Y As Byte, X As Byte, I As Byte, Z As Byte, Chislo(6) As Integer, Channel_1 As Integer, On1 As Word, Off1 As Word, Channel_2 As Integer, On2 As Word, Off2 As Word, Channel_3 As Integer, Sostoyanie As Byte, Sostoyanie_2 As Byte, Copy_print As Integer, Copy_print_sec As Integer, Copy_var As Integer, _print As Integer, _print_sec As Integer, Pokazaniya As Integer, View_menu As Byte, Booton_flag As Bit, Booton_flag_2 As Bit, Count As Word, Error_flag As Bit
Dim On2_l As Word, On2_h As Word, Off2_l As Word, Off2_h As Word, Diapazon As Byte
Dim Razryad_1 As Byte, Razryad_2 As Byte, Temp_najatiya As Byte, Indicator As Byte
Dim Menu_punkt As Byte 'пункт меню
Dim Set_flag As Bit 'флаг режима настройки уставок
Dim Podskaz_flag As Bit, Podskaz_flag_sec As Bit 'флаги режима отображения подсказок на соответствующих каналах
Dim Default As Eram Byte At &H14 'переменная EEPROM для записи начальных уставок для первого включения прибора
'A Alias Portd.5: B Alias Portd.7: C Alias Portd.3: D Alias Portd.1 'порты, к которым подключены сегменты индикатора
'E Alias Portd.0: F Alias Portd.6: G Alias Portd.4: H Alias Portd.2 '
'Dig1 Alias Portb.0: Dig2 Alias Portb.1: Dig3 Alias Portb.2 ' порты, к которым подключены общие аноды 1го индикатора
'Dig1_sec Alias Portb.5: Dig2_sec Alias Portb.6: Dig3_sec Alias Portb.7 ' порты, к которым подключены общие аноды 2го индикатора
A Alias Portd.2: B Alias Portd.6: C Alias Portb.7: D Alias Portb.2 'порты, к которым подключены сегменты индикатора
E Alias Portd.0: F Alias Portb.6: G Alias Portd.7: H Alias Portb.5 '
Dig1 Alias Portd.3: Dig2 Alias Portd.4: Dig3 Alias Portd.1 ' порты, к которым подключены общие аноды 1го индикатора
Dig1_sec Alias Portd.5: Dig2_sec Alias Portb.0: Dig3_sec Alias Portb.1 ' порты, к которым подключены общие аноды 2го индикатора
For X = 1 To 6 ' присваиваем всем цифрам пустоту, чтоб в момент включения не высвечивались нули
Chislo(x) = 11
Next
'*********** ___ Переключение типа индикаторов (ОК/ОА) ___ *********************
Readeeprom Indicator, 30
If Vibor = 0 Then ' Удерживая кнопку УСТ, подаем питание.
For X = 1 To 200
If X = 150 Then ' При длительном нажатии
Set Booton_flag
Toggle Indicator.3 ' Переключаем младший (четвертый) бит переменной. Номер бита выбрал произвольно
Writeeeprom Indicator, 30
If Indicator = &B00000000 Then ' Если переключили на ОА, зажжем все сегменты идикатора для подтверждения
Reset A: Reset B: Reset C: Reset D: Reset E: Reset F: Reset G: Reset H
Set Dig1: Set Dig2: Set Dig3: Set Dig1_sec: Set Dig2_sec: Set Dig3_sec
End If
If Indicator = &B00001000 Then ' Если переключили на ОK, зажжем все сегменты идикатора для подтверждения
Set A: Set B: Set C: Set D: Set E: Set F: Set G: Set H
Reset Dig1: Reset Dig2: Reset Dig3: Reset Dig1_sec: Reset Dig2_sec: Reset Dig3_sec
End If
Waitms 1500
Exit For
End If
If Vibor = 1 Then Exit For
Waitms 10
Next
End If
'*******************************************************************************
If Default = 255 Then 'при первом запуске, когда Default=255
Off1 = 120 'присваиваем начальный уставки
On1 = 110
' Off2 = 60
' On2 = 50
On2_l = 800
On2_h = 99
Off2_l = 600
Off2_h = 99
Indicator = &B00000000 ' для ОА. Indicator = &B00001000 для ОК
Razryad_1 = &B10111011
Razryad_2 = &B11101110
Writeeeprom On1, 0 ' Записываем в EEPROM уставку ON1
Writeeeprom Off1, 5 ' Записываем в EEPROM уставку OFF1
' Writeeeprom On2, 10 ' Записываем в EEPROM уставку ON2
' Writeeeprom Off2, 14 ' Записываем в EEPROM уставку OFF2
Writeeeprom On2_l, 22
Writeeeprom On2_h, 24
Writeeeprom Off2_l, 26
Writeeeprom Off2_h, 28
Writeeeprom Razryad_1, 16
Writeeeprom Razryad_2, 17
Writeeeprom Indicator, 30
Default = 100 ' присваиваем значение 100(произвольное, отличное от 255) и больше этот кусок кода выполняться не будет
End If
Readeeprom On1, 0 ' Считываем из EEPROM уставку ON1
Readeeprom Off1, 5 ' Считываем из EEPROM уставку OFF1
' Readeeprom On2, 10 ' Считываем из EEPROM уставку ON2
' Readeeprom Off2, 14 ' Считываем из EEPROM уставку OFF2
Readeeprom On2_l, 22
Readeeprom On2_h, 24
Readeeprom Off2_l, 26
Readeeprom Off2_h, 28
Readeeprom Razryad_1, 16
Readeeprom Razryad_2, 17
Config Timer0 = Timer, Prescale = 8: On Timer0 Pulse ' конфигурируем таймер 0 и назначаем подпрограмму которая выполняется при переполнении таймера
Config Timer1 = Timer, Prescale = 1: On Timer1 Bootons ' конфигурируем таймер 1 и назначаем подпрограмму которая выполняется при переполнении таймера
Enable Interrupts: Enable Timer0: Enable Timer1 ' разрешаем прерывания, таймер 0, таймер 1
Start Timer0: Start Timer1
Start Adc ' начало преобразования
If On2_l > 999 Then On2 = On2_h Else On2 = On2_l
If Off2_l > 999 Then Off2 = Off2_h Else Off2 = Off2_l
Do
If X > 40 Then ' увеличили период опроса АЦП, чтобы значения не прыгали
Stop Timer0: Stop Timer1 ' на время преобразования останавливаем таймеры
Channel_1 = Getadc(5) ' Вольтметр 0..500 В 'опрос АЦП (диапазон от 0 до 1023) (1 канал)
' Channel_1 = 1023 — Channel_1 'инвертирование раскомментировать===========
Channel_1 = Channel_1 / 2.046 'пересчет тут любая формула для требуемого диапазона или необходимой характеристики
If Channel_1 > 500 Then Channel_1 = 500 'верхний предел показаний
Channel_2 = Getadc(4) ' Ток 1,00… 9,99 А 'опрос АЦП (2 канал)
' Channel_2 = Channel_2 'пересчет тут любая формула для требуемого диапазона или необходимой характеристики
If Channel_2 > 999 Then Channel_2 = 999 'верхний предел показаний
' Razryad_2 = &B01110111
Diapazon = 2
If Channel_2 < 100 Then
Channel_2 = Getadc(3) ' Ток 0… 999 mА 'опрос АЦП (3 канал)
If Channel_2 > 999 Then Channel_2 = 999
' Razryad_2 = &B11101110
Diapazon = 1
End If
X = 0 '
End If
Start Timer0: Start Timer1
If On1 < Off1 Then 'если уставка ON1 < OFF1 то режим нагревателя
If Channel_1 <= On1 Then Sostoyanie = 1 'если значение первого канала АЦП ниже уставки ON1, то включаем нагрузку №1
If Channel_1 >= Off1 Then Sostoyanie = 0 'если значение первого канала АЦП выше уставки OFF1, то выключаем нагрузку №1
Else 'если уставка ON1 > OFF1 то режим охладителя
If Channel_1 >= On1 Then Sostoyanie = 1 'если значение первого канала АЦП выше уставки ON1, то включаем нагрузку №1
If Channel_1 <= Off1 Then Sostoyanie = 0 'если значение первого канала АЦП ниже уставки OFF1, то выключаем нагрузку №1
End If
If Sostoyanie = 1 Then Set Load1 Else Reset Load1 'управление 1м каналом нагрузки
'* Revision: 5.1 *
'* Controller: ATMEGA8 *
'* Compiler: BASCOM-AVR 2.0.6.1 *
'* Author: MACTEPOK *
'*******************************************************************************
$regfile = «m8def.dat» 'определяем контроллер
$crystal = 8000000 'внутренний генератор
'$sim
$lib «mcsbyte.lbx» 'подключаем библиотеку функций
Config Pinc.0 = Input: Portc.0 = 1 'кнопка Выбор
Config Pinc.1 = Input: Portc.1 = 1 'кнопка Вверх
Config Pinc.2 = Input: Portc.2 = 1 'кнопка Вниз
Config Portd = Output: Config Portb = Output 'порты на выход к которым подключен индикатор
Load1 Alias Portb.3 'нагрузка №1
Load2 Alias Portb.4 'нагрузка №2
Vibor Alias Pinc.0 'кнопка Выбор
Up Alias Pinc.1 'кнопка Вверх
Down Alias Pinc.2 'кнопка Вниз
Config Adc = Single, Prescaler = Auto, Reference = Avcc 'настраиваем АЦП
Dim W As Byte, Y As Byte, X As Byte, I As Byte, Z As Byte, Chislo(6) As Integer, Channel_1 As Integer, On1 As Word, Off1 As Word, Channel_2 As Integer, On2 As Word, Off2 As Word, Channel_3 As Integer, Sostoyanie As Byte, Sostoyanie_2 As Byte, Copy_print As Integer, Copy_print_sec As Integer, Copy_var As Integer, _print As Integer, _print_sec As Integer, Pokazaniya As Integer, View_menu As Byte, Booton_flag As Bit, Booton_flag_2 As Bit, Count As Word, Error_flag As Bit
Dim On2_l As Word, On2_h As Word, Off2_l As Word, Off2_h As Word, Diapazon As Byte
Dim Razryad_1 As Byte, Razryad_2 As Byte, Temp_najatiya As Byte, Indicator As Byte
Dim Menu_punkt As Byte 'пункт меню
Dim Set_flag As Bit 'флаг режима настройки уставок
Dim Podskaz_flag As Bit, Podskaz_flag_sec As Bit 'флаги режима отображения подсказок на соответствующих каналах
Dim Default As Eram Byte At &H14 'переменная EEPROM для записи начальных уставок для первого включения прибора
'A Alias Portd.5: B Alias Portd.7: C Alias Portd.3: D Alias Portd.1 'порты, к которым подключены сегменты индикатора
'E Alias Portd.0: F Alias Portd.6: G Alias Portd.4: H Alias Portd.2 '
'Dig1 Alias Portb.0: Dig2 Alias Portb.1: Dig3 Alias Portb.2 ' порты, к которым подключены общие аноды 1го индикатора
'Dig1_sec Alias Portb.5: Dig2_sec Alias Portb.6: Dig3_sec Alias Portb.7 ' порты, к которым подключены общие аноды 2го индикатора
A Alias Portd.2: B Alias Portd.6: C Alias Portb.7: D Alias Portb.2 'порты, к которым подключены сегменты индикатора
E Alias Portd.0: F Alias Portb.6: G Alias Portd.7: H Alias Portb.5 '
Dig1 Alias Portd.3: Dig2 Alias Portd.4: Dig3 Alias Portd.1 ' порты, к которым подключены общие аноды 1го индикатора
Dig1_sec Alias Portd.5: Dig2_sec Alias Portb.0: Dig3_sec Alias Portb.1 ' порты, к которым подключены общие аноды 2го индикатора
For X = 1 To 6 ' присваиваем всем цифрам пустоту, чтоб в момент включения не высвечивались нули
Chislo(x) = 11
Next
'*********** ___ Переключение типа индикаторов (ОК/ОА) ___ *********************
Readeeprom Indicator, 30
If Vibor = 0 Then ' Удерживая кнопку УСТ, подаем питание.
For X = 1 To 200
If X = 150 Then ' При длительном нажатии
Set Booton_flag
Toggle Indicator.3 ' Переключаем младший (четвертый) бит переменной. Номер бита выбрал произвольно
Writeeeprom Indicator, 30
If Indicator = &B00000000 Then ' Если переключили на ОА, зажжем все сегменты идикатора для подтверждения
Reset A: Reset B: Reset C: Reset D: Reset E: Reset F: Reset G: Reset H
Set Dig1: Set Dig2: Set Dig3: Set Dig1_sec: Set Dig2_sec: Set Dig3_sec
End If
If Indicator = &B00001000 Then ' Если переключили на ОK, зажжем все сегменты идикатора для подтверждения
Set A: Set B: Set C: Set D: Set E: Set F: Set G: Set H
Reset Dig1: Reset Dig2: Reset Dig3: Reset Dig1_sec: Reset Dig2_sec: Reset Dig3_sec
End If
Waitms 1500
Exit For
End If
If Vibor = 1 Then Exit For
Waitms 10
Next
End If
'*******************************************************************************
If Default = 255 Then 'при первом запуске, когда Default=255
Off1 = 120 'присваиваем начальный уставки
On1 = 110
' Off2 = 60
' On2 = 50
On2_l = 800
On2_h = 99
Off2_l = 600
Off2_h = 99
Indicator = &B00000000 ' для ОА. Indicator = &B00001000 для ОК
Razryad_1 = &B10111011
Razryad_2 = &B11101110
Writeeeprom On1, 0 ' Записываем в EEPROM уставку ON1
Writeeeprom Off1, 5 ' Записываем в EEPROM уставку OFF1
' Writeeeprom On2, 10 ' Записываем в EEPROM уставку ON2
' Writeeeprom Off2, 14 ' Записываем в EEPROM уставку OFF2
Writeeeprom On2_l, 22
Writeeeprom On2_h, 24
Writeeeprom Off2_l, 26
Writeeeprom Off2_h, 28
Writeeeprom Razryad_1, 16
Writeeeprom Razryad_2, 17
Writeeeprom Indicator, 30
Default = 100 ' присваиваем значение 100(произвольное, отличное от 255) и больше этот кусок кода выполняться не будет
End If
Readeeprom On1, 0 ' Считываем из EEPROM уставку ON1
Readeeprom Off1, 5 ' Считываем из EEPROM уставку OFF1
' Readeeprom On2, 10 ' Считываем из EEPROM уставку ON2
' Readeeprom Off2, 14 ' Считываем из EEPROM уставку OFF2
Readeeprom On2_l, 22
Readeeprom On2_h, 24
Readeeprom Off2_l, 26
Readeeprom Off2_h, 28
Readeeprom Razryad_1, 16
Readeeprom Razryad_2, 17
Config Timer0 = Timer, Prescale = 8: On Timer0 Pulse ' конфигурируем таймер 0 и назначаем подпрограмму которая выполняется при переполнении таймера
Config Timer1 = Timer, Prescale = 1: On Timer1 Bootons ' конфигурируем таймер 1 и назначаем подпрограмму которая выполняется при переполнении таймера
Enable Interrupts: Enable Timer0: Enable Timer1 ' разрешаем прерывания, таймер 0, таймер 1
Start Timer0: Start Timer1
Start Adc ' начало преобразования
If On2_l > 999 Then On2 = On2_h Else On2 = On2_l
If Off2_l > 999 Then Off2 = Off2_h Else Off2 = Off2_l
Do
If X > 40 Then ' увеличили период опроса АЦП, чтобы значения не прыгали
Stop Timer0: Stop Timer1 ' на время преобразования останавливаем таймеры
Channel_1 = Getadc(5) ' Вольтметр 0..500 В 'опрос АЦП (диапазон от 0 до 1023) (1 канал)
' Channel_1 = 1023 — Channel_1 'инвертирование раскомментировать===========
Channel_1 = Channel_1 / 2.046 'пересчет тут любая формула для требуемого диапазона или необходимой характеристики
If Channel_1 > 500 Then Channel_1 = 500 'верхний предел показаний
Channel_2 = Getadc(4) ' Ток 1,00… 9,99 А 'опрос АЦП (2 канал)
' Channel_2 = Channel_2 'пересчет тут любая формула для требуемого диапазона или необходимой характеристики
If Channel_2 > 999 Then Channel_2 = 999 'верхний предел показаний
' Razryad_2 = &B01110111
Diapazon = 2
If Channel_2 < 100 Then
Channel_2 = Getadc(3) ' Ток 0… 999 mА 'опрос АЦП (3 канал)
If Channel_2 > 999 Then Channel_2 = 999
' Razryad_2 = &B11101110
Diapazon = 1
End If
X = 0 '
End If
Start Timer0: Start Timer1
If On1 < Off1 Then 'если уставка ON1 < OFF1 то режим нагревателя
If Channel_1 <= On1 Then Sostoyanie = 1 'если значение первого канала АЦП ниже уставки ON1, то включаем нагрузку №1
If Channel_1 >= Off1 Then Sostoyanie = 0 'если значение первого канала АЦП выше уставки OFF1, то выключаем нагрузку №1
Else 'если уставка ON1 > OFF1 то режим охладителя
If Channel_1 >= On1 Then Sostoyanie = 1 'если значение первого канала АЦП выше уставки ON1, то включаем нагрузку №1
If Channel_1 <= Off1 Then Sostoyanie = 0 'если значение первого канала АЦП ниже уставки OFF1, то выключаем нагрузку №1
End If
If Sostoyanie = 1 Then Set Load1 Else Reset Load1 'управление 1м каналом нагрузки
' If On2 < Off2 Then 'если уставка ON2 < OFF2 то режим нагревателя
' If Channel_2 <= On2 Then Sostoyanie_2 = 1 'если давление ниже уставки ON2, то включаем нагрузку №2
' If Channel_2 >= Off2 Then Sostoyanie_2 = 0 'если давление выше уставки OFF2, то выключаем нагрузку №2
' Else 'если уставка ON2 > OFF2 то режим охладителя
' If Channel_2 >= On2 Then Sostoyanie_2 = 1 'если давление выше уставки ON2, то включаем нагрузку №2
' If Channel_2 <= Off2 Then Sostoyanie_2 = 0 'если давление ниже уставки OFF2, то выключаем нагрузку №2
' End If
If Diapazon = 2 Then
If Channel_2 >= On2_h Then Sostoyanie_2 = 1
If Channel_2 < Off2_h Then Sostoyanie_2 = 0
Else
If Channel_2 >= On2_l Then Sostoyanie_2 = 1
If Channel_2 < Off2_l Then Sostoyanie_2 = 0
End If
If Sostoyanie_2 = 1 Then Set Load2 Else Reset Load2 'управление 2м каналом нагрузки
Loop
'*******************************************************************************
' Индикация
Pulse:
If View_menu <> 0 And Error_flag = 0 Then Incr Count 'если находимся в меню и нет ошибки ввода уставок, то инкриментируем счетчик, который отвечает за автоматический выход из меню (~ через 5 сек)
If Count > 7000 Then 'задается время автоматического выхода из меню
Count = 0 'сброс счетчика
View_menu = 0 ' выход из меню в основной режим
End If
' H = 1 ' выключаем точку на индикаторе
Stop Timer0 'останавливаем таймер 0
Select Case View_menu 'в зависимости от пункта меню, записываем в переменные расчета следующие данные
Case 0:
_print = Channel_1 ' основной режим. 1й индикатор показывает значение АЦП 1го канала
_print_sec = Channel_2 ' основной режим. 2й индикатор показывает значение АЦП 2го канала
If Diapazon = 2 Then Razryad_2 = &B01110111 Else Razryad_2 = &B11101110
Case 1:
_print = On1 ' 1й индикатор показывает значение уставки ON1
_print_sec = Channel_2 ' 2й индикатор показывает значение АЦП 2го канала
Case 2:
_print = Off1 ' 1й индикатор показывает значение уставки OFF1
_print_sec = Channel_2 ' 2й индикатор показывает значение АЦП 2го канала
Case 3:
_print = Channel_1 ' 1й индикатор показывает значение АЦП 1го канала
If On2_l = 1000 Then Razryad_2 = &B01110111 Else Razryad_2 = &B11101110
_print_sec = On2 ' 2й индикатор показывает значение уставки ON2
Case 4:
_print = Channel_1 ' 1й индикатор показывает значение АЦП 1го канала
If Off2_l = 1000 Then Razryad_2 = &B01110111 Else Razryad_2 = &B11101110
_print_sec = Off2 ' 2й индикатор показывает значение уставки OFF2
End Select
If Podskaz_flag = 0 Then 'если не выводим на 1й индикатор подсказки, то работаем с числами
Copy_print = _print '
Copy_var = Copy_print '
For I = 3 To 1 Step -1 ' цикл в котором разбивается переменная на 3 числа
Chislo(i) = Copy_print Mod 10 ' заносим в масив последнюю цифру от числа Copy_print(123 mod 10 = 3)
Copy_print = Copy_print / 10 ' отсекаем последнюю цифру от числа Copy_print (123/10=12)
Next ' убираем незначимые нули
If Copy_var < 100 Then Chislo(1) = 11 'для 2х значного числа
' If Copy_var < 10 Then Chislo(2) = 11 'для однозначного числа
End If
If Podskaz_flag_sec = 0 Then 'если не выводим на 2й индикатор подсказки, то работаем с числами
Copy_print_sec = _print_sec '
Copy_var = Copy_print_sec
For I = 6 To 4 Step -1 ' цикл в котором разбивается переменная на 3 числа
Chislo(i) = Copy_print_sec Mod 10 ' заносим в масив последнюю цифру от числа _print(123 mod 10 = 3)
Copy_print_sec = Copy_print_sec / 10 ' отсекаем последнюю цифру от числа Copy_print_sec (123/10=12)
Next 'убираем незначимые нули
' If Copy_var < 100 Then Chislo(4) = 11 'для 2х значного числа
' If Copy_var < 10 Then Chislo(5) = 11 'для однозначного числа
End If
If Indicator = 0 Then ' Гасим индикаторы перед выводом информации
Reset Dig1: Reset Dig2: Reset Dig3: Reset Dig1_sec: Reset Dig2_sec: Reset Dig3_sec ' Для индикатора с ОА
Else
Set Dig1: Set Dig2: Set Dig3: Set Dig1_sec: Set Dig2_sec: Set Dig3_sec 'Для индикатора с ОK
End If
Incr W: If W > 6 Then W = 1 ' выбираем какую цифру сейчас включать
Y = 0
Gosub Look: A = Z ' переходим к подпрограмме Look, которая определяет нужно ли сейчас загорется сегменту А
Gosub Look: B = Z
Gosub Look: C = Z
Gosub Look: D = Z
Gosub Look: E = Z
Gosub Look: F = Z
Gosub Look: G = Z
Select Case W ' включаем цифру(разряд) которую выбрали (w). Подаем плюс на общий провод конкретной цифры (разряда)
Case 1:
If Indicator = 0 Then
Set Dig3
If Podskaz_flag = 0 Then H = Razryad_1.3 Else H = 1
Else
Reset Dig3
If Podskaz_flag = 0 Then
H = Razryad_1.3
Toggle H
Else
H = 0
End If
End If
Case 2:
If Indicator = 0 Then
Set Dig2
If Podskaz_flag = 0 Then H = Razryad_1.2 Else H = 1
Else
Reset Dig2
If Podskaz_flag = 0 Then
H = Razryad_1.2
Toggle H
Else
H = 0
End If
End If
Case 3:
If Indicator = 0 Then
Set Dig1
If Podskaz_flag = 0 Then H = Razryad_1.1 Else H = 1
Else
Reset Dig1
If Podskaz_flag = 0 Then
H = Razryad_1.1
Toggle H
Else
H = 0
End If
End If
Case 4:
If Indicator = 0 Then
Set Dig3_sec
If Podskaz_flag_sec = 0 Then H = Razryad_2.3 Else H = 1
Else
Reset Dig3_sec
If Podskaz_flag = 0 Then
H = Razryad_2.3
Toggle H
Else
H = 0
End If
End If
Case 5:
If Indicator = 0 Then
Set Dig2_sec
If Podskaz_flag_sec = 0 Then H = Razryad_2.2 Else H = 1
Else
Reset Dig2_sec
If Podskaz_flag = 0 Then
H = Razryad_2.2
Toggle H
Else
H = 0
End If
End If
Case 6:
If Indicator = 0 Then
Set Dig1_sec
If Podskaz_flag_sec = 0 Then H = Razryad_2.1 Else H = 1
Else
Reset Dig1_sec
If Podskaz_flag = 0 Then
H = Razryad_2.1
Toggle H
Else
H = 0
End If
End If
End Select
'
Start Timer0
Return
'
Look: ' подпрограмма которая определяет нужно ли сейчас гореть сегменту, который вызвал эту подпрограмму
Z = Chislo(w) * 7: Z = Y + Z ' определяем порядковый номер числа из таблици DATA. W — это цифра которую будем выводить 1..2..3, Y это номер сегмента (A=0 B=1 C=2...G=7)
If Indicator = 0 Then
Z = Lookup(z, Cifri_oa) ' выбираем из таблици включить или выключить нужный сегмент -OA
Else
Z = Lookup(z, Cifri_ok) '-OK
End If
Incr Y 'Y это номер сегмента (A=0 B=1 C=2...). Chislo(w) * 7 — переход на начало нужной строки Data.Z = Y + Z — по очереди перебираем сегменты в строке.
Return
' If Channel_2 <= On2 Then Sostoyanie_2 = 1 'если давление ниже уставки ON2, то включаем нагрузку №2
' If Channel_2 >= Off2 Then Sostoyanie_2 = 0 'если давление выше уставки OFF2, то выключаем нагрузку №2
' Else 'если уставка ON2 > OFF2 то режим охладителя
' If Channel_2 >= On2 Then Sostoyanie_2 = 1 'если давление выше уставки ON2, то включаем нагрузку №2
' If Channel_2 <= Off2 Then Sostoyanie_2 = 0 'если давление ниже уставки OFF2, то выключаем нагрузку №2
' End If
If Diapazon = 2 Then
If Channel_2 >= On2_h Then Sostoyanie_2 = 1
If Channel_2 < Off2_h Then Sostoyanie_2 = 0
Else
If Channel_2 >= On2_l Then Sostoyanie_2 = 1
If Channel_2 < Off2_l Then Sostoyanie_2 = 0
End If
If Sostoyanie_2 = 1 Then Set Load2 Else Reset Load2 'управление 2м каналом нагрузки
Loop
'*******************************************************************************
' Индикация
Pulse:
If View_menu <> 0 And Error_flag = 0 Then Incr Count 'если находимся в меню и нет ошибки ввода уставок, то инкриментируем счетчик, который отвечает за автоматический выход из меню (~ через 5 сек)
If Count > 7000 Then 'задается время автоматического выхода из меню
Count = 0 'сброс счетчика
View_menu = 0 ' выход из меню в основной режим
End If
' H = 1 ' выключаем точку на индикаторе
Stop Timer0 'останавливаем таймер 0
Select Case View_menu 'в зависимости от пункта меню, записываем в переменные расчета следующие данные
Case 0:
_print = Channel_1 ' основной режим. 1й индикатор показывает значение АЦП 1го канала
_print_sec = Channel_2 ' основной режим. 2й индикатор показывает значение АЦП 2го канала
If Diapazon = 2 Then Razryad_2 = &B01110111 Else Razryad_2 = &B11101110
Case 1:
_print = On1 ' 1й индикатор показывает значение уставки ON1
_print_sec = Channel_2 ' 2й индикатор показывает значение АЦП 2го канала
Case 2:
_print = Off1 ' 1й индикатор показывает значение уставки OFF1
_print_sec = Channel_2 ' 2й индикатор показывает значение АЦП 2го канала
Case 3:
_print = Channel_1 ' 1й индикатор показывает значение АЦП 1го канала
If On2_l = 1000 Then Razryad_2 = &B01110111 Else Razryad_2 = &B11101110
_print_sec = On2 ' 2й индикатор показывает значение уставки ON2
Case 4:
_print = Channel_1 ' 1й индикатор показывает значение АЦП 1го канала
If Off2_l = 1000 Then Razryad_2 = &B01110111 Else Razryad_2 = &B11101110
_print_sec = Off2 ' 2й индикатор показывает значение уставки OFF2
End Select
If Podskaz_flag = 0 Then 'если не выводим на 1й индикатор подсказки, то работаем с числами
Copy_print = _print '
Copy_var = Copy_print '
For I = 3 To 1 Step -1 ' цикл в котором разбивается переменная на 3 числа
Chislo(i) = Copy_print Mod 10 ' заносим в масив последнюю цифру от числа Copy_print(123 mod 10 = 3)
Copy_print = Copy_print / 10 ' отсекаем последнюю цифру от числа Copy_print (123/10=12)
Next ' убираем незначимые нули
If Copy_var < 100 Then Chislo(1) = 11 'для 2х значного числа
' If Copy_var < 10 Then Chislo(2) = 11 'для однозначного числа
End If
If Podskaz_flag_sec = 0 Then 'если не выводим на 2й индикатор подсказки, то работаем с числами
Copy_print_sec = _print_sec '
Copy_var = Copy_print_sec
For I = 6 To 4 Step -1 ' цикл в котором разбивается переменная на 3 числа
Chislo(i) = Copy_print_sec Mod 10 ' заносим в масив последнюю цифру от числа _print(123 mod 10 = 3)
Copy_print_sec = Copy_print_sec / 10 ' отсекаем последнюю цифру от числа Copy_print_sec (123/10=12)
Next 'убираем незначимые нули
' If Copy_var < 100 Then Chislo(4) = 11 'для 2х значного числа
' If Copy_var < 10 Then Chislo(5) = 11 'для однозначного числа
End If
If Indicator = 0 Then ' Гасим индикаторы перед выводом информации
Reset Dig1: Reset Dig2: Reset Dig3: Reset Dig1_sec: Reset Dig2_sec: Reset Dig3_sec ' Для индикатора с ОА
Else
Set Dig1: Set Dig2: Set Dig3: Set Dig1_sec: Set Dig2_sec: Set Dig3_sec 'Для индикатора с ОK
End If
Incr W: If W > 6 Then W = 1 ' выбираем какую цифру сейчас включать
Y = 0
Gosub Look: A = Z ' переходим к подпрограмме Look, которая определяет нужно ли сейчас загорется сегменту А
Gosub Look: B = Z
Gosub Look: C = Z
Gosub Look: D = Z
Gosub Look: E = Z
Gosub Look: F = Z
Gosub Look: G = Z
Select Case W ' включаем цифру(разряд) которую выбрали (w). Подаем плюс на общий провод конкретной цифры (разряда)
Case 1:
If Indicator = 0 Then
Set Dig3
If Podskaz_flag = 0 Then H = Razryad_1.3 Else H = 1
Else
Reset Dig3
If Podskaz_flag = 0 Then
H = Razryad_1.3
Toggle H
Else
H = 0
End If
End If
Case 2:
If Indicator = 0 Then
Set Dig2
If Podskaz_flag = 0 Then H = Razryad_1.2 Else H = 1
Else
Reset Dig2
If Podskaz_flag = 0 Then
H = Razryad_1.2
Toggle H
Else
H = 0
End If
End If
Case 3:
If Indicator = 0 Then
Set Dig1
If Podskaz_flag = 0 Then H = Razryad_1.1 Else H = 1
Else
Reset Dig1
If Podskaz_flag = 0 Then
H = Razryad_1.1
Toggle H
Else
H = 0
End If
End If
Case 4:
If Indicator = 0 Then
Set Dig3_sec
If Podskaz_flag_sec = 0 Then H = Razryad_2.3 Else H = 1
Else
Reset Dig3_sec
If Podskaz_flag = 0 Then
H = Razryad_2.3
Toggle H
Else
H = 0
End If
End If
Case 5:
If Indicator = 0 Then
Set Dig2_sec
If Podskaz_flag_sec = 0 Then H = Razryad_2.2 Else H = 1
Else
Reset Dig2_sec
If Podskaz_flag = 0 Then
H = Razryad_2.2
Toggle H
Else
H = 0
End If
End If
Case 6:
If Indicator = 0 Then
Set Dig1_sec
If Podskaz_flag_sec = 0 Then H = Razryad_2.1 Else H = 1
Else
Reset Dig1_sec
If Podskaz_flag = 0 Then
H = Razryad_2.1
Toggle H
Else
H = 0
End If
End If
End Select
'
Start Timer0
Return
'
Look: ' подпрограмма которая определяет нужно ли сейчас гореть сегменту, который вызвал эту подпрограмму
Z = Chislo(w) * 7: Z = Y + Z ' определяем порядковый номер числа из таблици DATA. W — это цифра которую будем выводить 1..2..3, Y это номер сегмента (A=0 B=1 C=2...G=7)
If Indicator = 0 Then
Z = Lookup(z, Cifri_oa) ' выбираем из таблици включить или выключить нужный сегмент -OA
Else
Z = Lookup(z, Cifri_ok) '-OK
End If
Incr Y 'Y это номер сегмента (A=0 B=1 C=2...). Chislo(w) * 7 — переход на начало нужной строки Data.Z = Y + Z — по очереди перебираем сегменты в строке.
Return
Комментарии (43)
RSS свернуть / развернуть