Привет колеги!
Платката ми е ок вече, но се случи случка с фотоапарата ни. Явно е заложена повреда в него, приятелка фотографка ми каза, че била чест проблем. Една от мъничките предавки в системата за оптичен зум явно е изгубила няколко зъбчета и сега понякога не ми се прибира оптиката до край при гасене, понякога като зумна до край и после върна - имам тъмно петно в дясната част на снимките. Това ни е семейния апарат, нямам пари за друг сега, нито за ремонти, така че ще правя само по някоя снимка от време на време и ще гледам да не го мъча и да не го разнасям с мен до работата постоянно, където снимам филмчетата.
За сега спирам със снимките, но ще Ви предложа писмено обяснение и сорс код за последния епизод от тази серия - АЦП.
Тази програмка измерва напрежението на аналоговия вход и прави с резултата две неща - показва нивото му на светодиодната стълбица и предава стойностите към компютъра през серийния порт. Тъй като светодиодите са 8 на брой и АЦП се използва в 8-битов режим (0-255), то стъпката на стълбицата е 0.625V на стъпка. Стъпката на напрежението през серийния порт е по-фина - 0.195V. Нека погледнем кода:
-- ------------------------------------------------------
-- Title: BOb ADC SERIAL
-- Author: Billian Marinov
-- Compiler: 2.4q4
-- Revision: 0.1
-- Description: LED/SERIAL 8 bit voltmeter
-- Notes: 0.625V/LED step, 0.195V/serial step
-- ------------------------------------------------------
include 16f88
pragma target clock 20_000_000
pragma target OSC HS
pragma bootloader bloader
include print
include format
Ок, до тук всичко е както в предните случаи.
-- ------------------------------------------------------
enable_digital_io() -- enable ditital IO on ports
const serial_hw_baudrate = 1_200 --set your baudrate here
include serial_hardware
serial_hw_init() --initialize the serial module
-- ------------------------------------------------------
const byte ADC_NVREF = ADC_NO_EXT_VREF -- using internal Vref
const bit ADC_HIGH_RESOLUTION = false -- using 8 bit mode on ADC
include adc
adc_init() -- initialize ADC
set_analog_pin(0) -- set A0 as analog input pin
-- ------------------------------------------------------
Инициализираме ифровите входове/изходи, избираме скорост за комуникацията 1.2кбит/с. След това идва новото - инициализацията на АЦП модула. Стъпките са няколко. Както при всяко друго АЦП трябва да изберем дали ще позваме какво референтно напрежение ще използваме. Тъй като ще мерим до 5 волта, можем да позлваме вътрешния източник, който практически е захранващото напрежение. За да заявим нашия избор на библиотеката, която ще управлява АЦП-то, дефинираме байт констнта ADC_NVREF като ѝ даваме текстова стойност ADC_NO_EXT_VREF (без външно референтно напрежение).
В 16F88 има вграден 10 битов АЦП модул, кйто ни дава разделителна способност от 1023 точки. Това е хубаво и означава, че теоритично можем да измерваме с точност около 5 миливолта, но има един малък проблем - микроконтролера е 8 битов и резултата от измерването ще ни бъде върнат в два байта, като в горния ще имаме 2-та допълнителни бита. За наште цели не само, че такава точност е ненужна, но и сметките с пренасяне в два байта са твърде сложни. Точно за такива моменти е предвидена възможност АЦП модула да се ползва с ниска резолюция от 8 бита, което ще ни позволи лесни сметки и резултат само в един байт. За да заявим този избор дефинираме бит константа ADC_HIGH_RESOLUTION и ѝ даваме стойност false.
След това добавяме библиотеката, инициализираме модула и избираме кой от входовете да измерваме. В този случай съм избрал А0, защото на развойната платка това ми е единствения защитен вход на клема, но има достъп и до други, ако е нужно. Този избор може да бъде правен по всяко време в кода.
alias DI1 is pin_A5
alias DI2 is pin_A4
alias DIO1 is pin_B0
alias DIO2 is pin_B1
alias DIO3 is pin_B3
alias DIO4 is pin_B4
alias DIO5 is pin_B6
alias DIO6 is pin_B7
alias DIO7 is pin_A1
alias DIO8 is pin_A2
-- ------------------------------------------------------
pin_B0_direction = output
pin_B1_direction = output
pin_B3_direction = output
pin_B4_direction = output
pin_B6_direction = output
pin_B7_direction = output
pin_A1_direction = output
pin_A2_direction = output
pin_A4_direction = input
pin_A5_direction = input
Тук правим традиционните настройки на изходите и входовете, познати ви от предните епизоди. Нека сега извършим някои необходими дефиниции за нашата програма:
const byte str1[] = "RCe100 ADC Voltage: "
var word measure = 0
dio1 = 0 dio2 = 0 dio3 = 0 dio4 = 0 dio5 = 0 dio6 = 0 dio7 = 0 dio8 = 0
Създаваме текстов стринг str1[], който ще изпращаме през серийния порт. Създаваме променлива байт measure, в който библиотеката за АЦП-то ще ни връща резултата от измерванията. Нулираме 8-те изхода за светодиодите. Време е да създадем главния цикъл!
forever loop
-- *****************************************************************************
measure = adc_read_low_res(0)
_usec_delay(100)
if measure >= 32 then dio1 = 1 else dio1 = 0 end if
if measure >= 64 then dio2 = 1 else dio2 = 0 end if
if measure >= 96 then dio3 = 1 else dio3 = 0 end if
if measure >= 128 then dio4 = 1 else dio4 = 0 end if
if measure >= 160 then dio5 = 1 else dio5 = 0 end if
if measure >= 192 then dio6 = 1 else dio6 = 0 end if
if measure >= 224 then dio7 = 1 else dio7 = 0 end if
if measure >= 255 then dio8 = 1 else dio8 = 0 end if
measure = measure * 196
print_string(serial_hw_data, str1)
format_word_dec(serial_hw_data, measure, 6, 4)
serial_hw_data = ASCII_CR
_usec_delay(200_000)
-- *****************************************************************************
end loop
Ето я и основната част от нашата програма - измервателния цикъл. В началото извършваме измерване, като на променливата measure риравняваме функцията adc_read_low_res(0) от библиотеката ADC. Това действие предизвиква измервателен цикъл, на края на който в measure получаваме измерената стойност. Тя е под формата на число от 0 до 255. Изчакваме 100 микросекунди АЦП модула да си свърши работата.
Следват функциите за управление на светодиодната стъбица. Чрез тях преценяваме нивото на напрежението и запалваме определения брой светодиоди. Използват се структури IF/THEN/ELSE, като обхвата е разделен на 8 части.
За да изобразим напрежението на компютърния екран трябва първо да превърнем тази байтова стойност в смисленото за човека напрежение във волтове. Правим това, като умножаваме получения резултат със стойността на стъпката от 19.6 миливолта, т.е. ако имаме резултат 100 като измерване, това ще означава, че напрежението е 1.96 волта. За да не правим умножение на числа със запетая, което изисква дефиниране на друг тип променливи и константи, съм умножил с 196, като за десетичната запетая мога да се погрижа в последствие чрез параметрите на функцията print. Както виждате извършва реверсивно умножение, т.е. умножавам measure с 196 и записвам резултата обратно в measure. Тази опция в JALv2 позволява спестяване на една променлива и няколко реда код. Резултата от горния пример би бил 100*196=19600.
След това, използвайки print_string изпращаме към екрана на компютъра надписа "RCe100 ADC Voltage: ". Веднага след това, използвайки функцията format_word_dec (форматиране на десетична дума) форматираме резултата като число с 6 знака (заедно с точката), като разполагаме точката на 4 позиция от края. Това би оформило числото 19600, което дадохме за пример горе, като 1.9600, точната стойност, и я изпращаме към серийния порт.
Ако оставим обаче нещата така, вместо да виждаме на екрана един ред, например
RCe100 ADC Voltage:1.9600, ще се получи един водопад от такива редове, пълнещ екрана и правещ наблюдението почти невъзможно. По тази причина след резултата изпращаме стандартната ASCii команда CR, или връщане на каретката (carriage return). Тя връща курсора в горния ляв край на екрана, за да може следващото измерване да бъде изписано
върху старото, предотвратявайки задръстването на екрана.
Изчакваме 200 милисекунди и започваме отново. Защо правим това - оставям на вас!
Това е програмата, колеги!
Ще се опитам да направя кратко видео с работещия код и да го кача, за нагледен пример.