Нарушая запреты. Опыт программирования. Часть 2
Есть два типа разработчиков. Первые - до начала работ все детально продумывают. Долго ничего не делают, но потом быстро все реализуют. Вторые начинают делать все сразу, по принципы, начнем, а там видно будет. Зачастую, им приходится переделывать уже сделанное и на все это уходит масса времени и средств. У обоих подходов есть свои плюсы и минусы. Будем придерживаться первого подхода и вначале все обдумаем.
В основе разработки лежало описание измерителя емкости конденсатора(демонстрационная версия) из переводной старой книги по применению первых персональных или даже малогабаритных компьютеров в целях измерения и управления на примере системы типа Lego.
Предлагалось:
1. Разрядить конденсатор, закоратив его с помощью реле.
2. Замкнуть цепь зарядки и зарядить конденсатор.
3. Переключить конденсатор на резистор и разрядить его компаратором контролируя момент разряда до нуля. При этом считать время разряда.
4. Время заряда будет пропорционально емкости конденсатора.
После некоторого рассуждения, пришел к выводу, что с компаратором лучше не связываться, т.к. неизвестно, как поведет себя компаратор микроконтроллера в районе нуля вольт. Второе, с целью обеспечения безопасности микроконтроллера, в любом случае подключаемый конденсатор должен быть разряжен. Это неизбежно влечет применение реле, которое в обесточенном состоянии и в начальном состоянии замыкает своими контактами проверяемый конденсатор. И третье, по многим причинам, что станет ясно в дальнейшем, лучше измерять время заряда конденсатора, а не разряда.
Для перехода от измеряемого времени к емкости конденсатора пришлось открыть учебник по ТОЭ Атабекова и найти там нужную формулу. Эту страшную формулу даже не потребовалось считать, т.к. здесь же была приведена таблици в которой было ясно указано через сколько постоянных времени RC конденсатор зарядится до определенного значения Ex, где Ех < E - напряжение (ЭДС) источника питания. Т.к. напряжение питания МК +5В, то порог Ех был выбран порядка 4В. Использовать для создания этого порога на входе компаратора прецизионные резисторы показалось совсем уж дикой затеей и тогда пришла в голову идея измерять напряжение на конденсаторе встроенным в МК АЦП.
Что пришлось считать и проверять: частоту прерываний МК для подсчета времени зарядки конденсатора, величину R (10 кОм), преобразование кода в напряжение в АЦП, т.к. оказалось возможным выводить в процессе измерения емкости измеренное на конденсаторе напряжение на ЖКИ. Минус метода - время измерения зависит от емкости конденсатора и изменяется от долей секунды (0,1 мкФ) до бесконечности при измерении максимальной емкости равной бесконечности. Пришлось ввести кнопку старта измерения, чтобы до старта контакты реле строго замыкали выводы конденсатора.
На практике это ограничение оказалось не существенным. Погрешность измерения без подстройки программы составила -7 процентов.
Но самое поразительное, что данным прибором оказалось возможным браковать емкости, которые другими промышленными цифровыми приборами были отмечены как абсолютно годные. Для объяснения этого результата пришлось углубленно посмотреть на процедуру заряда конденсатора с различными дефектами. Реальный конденсатор мы должны рассматривать как емкость, параллельно и последовательно с которой включены паразитные резисторы, влияние которых и обнаруживается при данном методе измерений.
Ниже приведен текст программы. Куда чего подключено понятно из него. Используется макет программы из пакета, в предедущей части.
Файл stdiodemo.c
/*
* * "THE BEER-WARE LICENSE" (Revision 42):
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
* *
* Stdio demo
*
* $Id: stdiodemo.c,v 1.1.2.1 2005/12/28 22:35:08 joerg_wunsch Exp $
*/
#include "defines.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include "lcd.h"
#include "uart.h"
void adc_init(void);
static void delay_1s(void);
static volatile int32_t t=0;
static volatile int32_t tt=0;
static volatile int32_t tc=0;
static int32_t m_tc=0;
static volatile int FlStart=0;
ISR(INT0_vect)
{
FlStart=1;
}
ISR(TIMER1_COMPA_vect) // 1 мсек прерывания (16-ти разрядный TIMER1), см. AvrCalc.exe, перезапускать таймер не надо
{
if ((t>3)&&(tt<3160)) ++tc; // конденсатор разряжен - не измеряем до 3,16B
// if (tt<3160) // 3,16 B
ADCSRA |= _BV(ADSC); // начало преобразования ADC Start Conversion = 1
}
ISR(ADC_vect)
{
t=(int32_t)ADCW;
tt = t*4900L/1024L; // в мВ
}
/*
* Do all the startup-time peripheral initializations.
*/
static void ioinit(void)
{
uart_init();
lcd_init();
adc_init();
TIMSK = _BV(OCIE1A); // разр. прер. T1 compare OCIE1A флаг разрешения прерывания по события "Совпадение А" таймера счетчика Т1
TCNT0 = 0; //
SFIOR |= _BV(PSR10); // сброс прескаллера, в 0 вернется автоматически
TCCR0 = 5; // установим прескалер тaймера T0 fclk/1024. прескалером Т1 управляют по даташиту с. 82, с.107, с.108 разряды CS10,CS11,CS12 (b0-b2) регистра TCCR1B
PORTD |= _BV(PD2); // подключим внутренний резистор 20...50 кОм к PD2 (INT0) кнопка
DDRD &= ~_BV(PD2); // PD2 (INT0) - сконфигурируем как вход (белая кнопка)
PORTD &= ~_BV(PD6); // выведем 0 - конденсатор разряжен - реле не включено
DDRD |= _BV(PD6); // PD6 - сконфигурируем как выход
MCUCR = 0X0a; // прерывание по спаду на INT1,0
GICR |= _BV(INT0); // Разрешение прерывания INT0
/* подробно установки таймера Т1 см. файл readmy.txt */
// используем программу AvrCalc.exe для кварца 11,059 МГц и выкл прескалере получаем на таймере 1
//получим то что записали в OCR1AH и OCR1AL
OCR1AH = 0x2b; // для 1 mсек при прескалере 1:1
OCR1AL = 0x33; // 11059 значение
TCCR1A = 0; // 4-я мода и выводы счетчика отключены и все в норме
TCCR1B = 0x09; // 1001 = 0x01 (х/1) + 0x08 - (мода 4 СТС) - сброс в 0 при достижении значения в OCR1A
TIFR = 0xFF; // сбросим все флаги прерываний записью в этот регистр единиц с. 110 даташита и евстифеев
GIFR = 0xFF; // сбросим флаги прерываний INT0, INT1, INT2 - даташит с. 66
ACSR=0x80; // компаратор выключить
}
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
FILE lcd_str = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
static void
delay_1s(void)
{
uint8_t i;
for (i = 0; i < 100; i++)
_delay_ms(10);
}
/*
* Инициализация АЦП
* Вывод AREF (pin 29) через ЧИП конденсатор 0,1 мк соединен с землей AGND (pin 28).
* 1. внутренний источник опорного напряжения
* 2. работа в режиме одиночных преобразований (Free Running Mode) ADATE=0 (после сброса)
* 3. для нулевого канала ADMUX = 0 (пока)
* 4. входы АЦП ADC0/PA0 (pin 37), ADC1/PA1 (p.36), ADC2/PA2 (p.35), ADC3/PA3 (p.34), ADC4/PA4 (p.33), ADC5/PA5 (p.32), ADC6/PA6 (p.31), ADC7/PA7 (p.30) - используется только ADC0/PA0 (pin 37)
* 5. результат ADCH(старшие 2 бита), ADCL(младшие 2 бита)
* 6. управление делителем (скоростью преобразования) ADCSRA
* 7. диапазон Твыб=65:260 мкс (Fд=4:15 кГц).
Для управления АЦП в ATmega16 применяются всего 3 регистра:
ADCSRA - регистр управления и состояния
ADMUX - регистр управления мультиплексором
SFIOR - регистр специальных функций
*
void adc_init(void) {
DDRA = 0; // у АЦП всe выводы порта РА - входы АЦП (8 шт) выведены на плате на контакты под пайку
ADMUX = 0x40;
ADCSRA &= ~_BV(ADPS0);//ADPS0=0
ADCSRA |= _BV(ADPS1); //ADPS1=1
ADCSRA |= _BV(ADPS2); //ADPS2=1, т.е. 6 (делитель на 64) - частота выборок > 10 кГц K[64]=Fclk[11059кГц]/(13*Fд[кГц]) см. Datasheets с.214 тактовая АЦП почти 200 кГц (максимальная)
ADCSRA |= _BV(ADIF); // очистка флага окончания преобразования
ADCSRA |= _BV(ADIE); //ADIE=0 - прерывания АЦП
ADCSRA |= _BV(ADEN); // включить АЦП
// произвести первое чтение до начала работы
ADCSRA |= _BV(ADSC); // начало преобразования ADC Start Conversion = 1
while (bit_is_clear(ADCSRA, ADIF)); // ожидания флага окончания преобразования
ADCSRA |= _BV(ADIF); // очистка флага окончания преобразования
}
int main(void)
{
ioinit();
stdout = stdin = &uart_str;
stderr = &lcd_str;
PORTD &= ~_BV(PD6); // выведем 0 - конденсатор разряжен - реле не включено
fprintf_P(stderr,PSTR( " C - meter \n"));
delay_1s();
while (bit_is_set(PIND, PD2));
_delay_ms(10);
while (bit_is_clear(PIND, PD2));
fprintf_P(stderr,PSTR( "Start conversion\n"));
delay_1s();
sei(); // enable all interrupts
PORTD |= _BV(PD6); // выведем 1 - конденсатор на заряд - реле включено
_delay_ms(1);
FlStart=0;
while(1)
{
if (tt>=3160) {
cli(); // disable all interrupts
PORTD &= ~_BV(PD6); // выведем 0 - конденсатор разряжен - реле не включено
if (bit_is_clear(PIND, PD2)) FlStart=1;
lcd_gotop();
m_tc = tc*94L/100L;
// fprintf(stderr,"C = , uF ", (int16_t)(tc/10L),abs((int16_t)(tc))); // for R = 10 kOm
fprintf(stderr,"C = , uF ", (int16_t)(m_tc/10L),abs((int16_t)(m_tc))); // for R = 10 kOm
}else{
lcd_gotop();
fprintf(stderr,"Uc = , B ", (int16_t)(tt/1000L), abs((int16_t)(tt)));
}
if (FlStart==1) {
fprintf_P(stderr,PSTR( "\n Start measure \n"));
delay_1s();
delay_1s();
t=0;
tc=0;
tt=0;
sei(); // enable all interrupts
PORTD |= _BV(PD6); // выведем 1 - конденсатор на заряд - реле включено
_delay_ms(1);
FlStart=0;
};
}
return 0;
}
Файл
Definse.h
*
* * "THE BEER-WARE LICENSE" (Revision 42):
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
* *
* General stdiodemo defines
*
* $Id: defines.h,v 1.1.2.1 2005/12/28 22:35:08 joerg_wunsch Exp $
*/
#include <avr/pgmspace.h>
/* CPU frequency */
#define F_CPU 11059000UL
//#define F_CPU 2560000UL // error +1,1 %
/* UART baud rate */
#define UART_BAUD 9600 /* 9600 */
#define UART_PORTOUT PORTD
#define UART_PORTIN PIND
/* ADC */
#define ADC_PORTIN PINA
#define ADC0 PORT0 /* pin сonnection of capacitor Cx for C-meter */
/* HD44780 LCD port connections */
#define HD44780_PORT C
#define HD44780_RS PORT0
#define HD44780_RW PORT1
#define HD44780_E PORT2
#define HD44780_D4 PORT4
#define HD44780_D5 PORT5
#define HD44780_D6 PORT6
#define HD44780_D7 PORT7
Как все делалось:
16.11.2009 План работы.
Первый этап. Создал новый проект все на той же основе . Оставил только вывод
на индикатор. В качестве железа использовать плату от проекта
пятиканального термометра.
Второй этап. Добавить прерывание на 1 мс.
Третий этап. Сделать измерение с выводом на ЖКИ.
Четвертый этап. Сделать измерение с выводом на ЖКИ в прерывании.
Пятый этап. Сделать накопление результатов.
Шестой этап. Сделать передачу накопления в компьютер. Обработку там измерения
и выдачу результатов (писать на
Седьмой этап. Сделать обсчет в ATMega16 с выводом на ЖКИ.
Восьмой этап. Сделать кнопку. Обвесить железом. Сделать корпус (использовать
от школьного компьютера). Дырку закрыть черной пласмассой с
отверстием под индикатор.
Девятый этап. Добавить электротренировку конденсаторов. Использовать преобра-
зователь напряжения (импортный). Питать все от
20.11.2009 Реализовано измерение через 1 мс. Сделано на таймере 1 в спец. режиме
с автоматич. сбросом счетчика в 0. Производится просто перезапуск АЦП
каждую 1 мс. МАХ измеряемое значение 1023 при 2,54 В на входе АЦП.
Ушел от 32 разрядных переменных.
8.01.2015 Output C*96/100 = 0,96 C (uF) = C - 6 (uF) - калибровка С-метра
Желающим подробнее узнать о работе конденсатора, могу рекомендовать порыться в интернете и найти журналы по радио 20-х годов.
Свидетельство о публикации №217011600799