Среднечастотный частотомер на AVR. Часть 3, + милливольтметр.

  Это продолжение предыдущих частей "Часть1, динамическая индикация" и "Часть2, статическая индикация".
  В первой части я посетовал, что в ATmega8A при занятой памяти менее 10% почти не осталось свободных ножек. Во второй части я эту проблему решил с помощью внешнего контроллера дисплея. Осталось придумать, куда использовать освободившиеся ножки и неиспользованную память.
  Для измерительного генератора эти ресурсы могут быть применены при осовременивании схемы. Например замены переменного резистора настройки на инкрементальный энкодер, замены механических переключателй на управление реле или бесконтактными ключами и т.д. Это все индивидуально для каждой схемы. Но как правило измерительный генератор имеет регулируемый по напряжению выход. Контролировать уровень напряжения на нем также желательно. А у нас как раз остались незадействованными 6 каналов ADC (для PDIP, в корпусе TQFP их 8). Поэтому введем в программу второй канал измерения, измерять будем напряжение на входе ADC.

  Я буду приводить куски программы с описанием для лучшего понимания работы. Для этого желательно прочитать и первые две части. Полные исходники можно скачать в конце статьи. К исходникам приложен проект Proteus 7.10 с симуляцией работы программы. Статья описывает особенности примененных кодовых решений, схемотехническое построение и схему подключения можно посмотреть на схеме Proteus. Сразу скажу, на схеме Proteus могут отсутствовать некоторые детали, не нужные для симуляции, но обязательные при изготовлении конструкции. Но ориентироваться на нее можно, тем более статья не расчитана на новичков в схемотехнике AVR, больше на программистов.

Для кода применялся тот же включаемый файл FQMacros.inc, что и в первой части.

Дисплей с торговой маркой TIC + милливольтметр.

  Этот вариант кода предназначен для 9-разрядных дисплеев TIC71 (TIC265). Т.к. кол-во каналов измерения в программе удвоилось, одного 8-битного флагового регистра уже не хватает. Чтобы не транжирить ценные регистры из «верхней» половины, регистры флагов взяты из неиспользуемого блока TWI:

;-------------------------------------------------------------------------------;
.EQU    PIN_VOLT    = PortC0        ; вход ADC
.EQU    PIN_T1      = PortD5        ; вход частотомера
.EQU    PIN_DCLK    = PortD6        ; выход на дисплей
.EQU    PIN_LOAD    = PortD7        ; выход на дисплей
.EQU    PIN_DOUT    = PortB0        ; выход на дисплей
.EQU    PIN_LCLK    = PortB1        ; выход на дисплей
;-------------------------------------------------------------------------------;
.EQU    PIN_BTN     = PinD2         ; вход кнопки
.EQU    NUM_CKEY    = 2             ; антидребезговый интервал кнопки
.EQU    CountADC    = 10            ; периодичность замера вольтметра
;------ Предназначение регистров -----------------------------------------------;
.DEF    TimerADC    = R0            ; счетчик запуска замера вольтметра
.DEF    FxOVF       = R1            ; Старший байт частоты (счетный)
.DEF    Safe        = R16           ; Сохранение SREG в прерывании
.DEF    Temp        = R17           ; Временный регистр
.DEF    Cycler      = R18           ; Временный регистр
.DEF    KeyPress    = R19           ; счетчик опросов удержания кнопки
.EQU    BITx1       = TWAR          ; Битовый регистр 1
.EQU    MRK_BCDF    = 0             ; Флаг преобразования FREQ PackBCD <- BIN
.EQU    MRK_B2BF    = 1             ; Флаг преобразования FREQ UnpackBCD <- PackBCD
.EQU    MRK_SymF    = 2             ; Флаг преобразования FREQ Symbols  code <- UnpackBCD
.EQU    MRK_RdyF    = 3             ; Флаг готовности результата FREQ
.EQU    MRK_Disp    = 4             ; Флаг вывода результата на индикацию (FREQ или VOLT)
.EQU    MRK_SPI     = 5             ; Флaг вывода одного бита индикации
.EQU    MRK_Key     = 6             ; Флaг опроса клавиатуры
.EQU    MRK_Btn     = 7             ; Флaг/триггер нажатия и отпускания кнопки
.EQU    BITx2       = TWBR          ; Битовый регистр 2
.EQU    MRK_VOLT    = 0             ; Флaг разрешения измерений вольтметра
.EQU    MRK_eADC    = 1             ; Флaг обработки результата ADC
.EQU    MRK_BCDV    = 2             ; Флаг преобразования VOLT PackBCD <- BIN
.EQU    MRK_B2BV    = 3             ; Флаг преобразования VOLT UnpackBCD <- PackBCD
.EQU    MRK_SymV    = 4             ; Флаг преобразования VOLT Symbols  code <- UnpackBCD
.EQU    MRK_RdyV    = 5             ; Флаг готовности результата VOLT

  Второй канал измерения напряжения повторяет переменными первый канал измерения частоты. Изменилась размерность, вместо трех байт FREQ стало два VOLT.

;----- Резервирование ячеек ОЗУ ------------------------------------------------;
.DSEG
.ORG SRAM_START
    RAM_Val_L:  .byte   1           ; Измеренное значение FREQ, младший байт
    RAM_Val_M:  .byte   1           ; Измеренное значение FREQ, средний байт
    RAM_Val_H:  .byte   1           ; Измеренное значение FREQ, старший байт
BegBCDF:
    RAM_BCD_0:  .byte   1           ; Упакованный BCD FREQ, 1 байт
    RAM_BCD_1:  .byte   1           ; Упакованный BCD FREQ, 2 байт
    RAM_BCD_2:  .byte   1           ; Упакованный BCD FREQ, 3 байт
    RAM_BCD_3:  .byte   1           ; Упакованный BCD FREQ, 4 байт
EndBCDF:
.EQU	SizeBCDF = EndBCDF - BegBCDF
    RAM_UnBCDF: .byte   BufSize     ; Неупакованные BCD FREQ
    RAM_SmblsF: .byte   BufSize     ; Коды цифр FREQ (symbols)
; - - - - - - - - - - - - - - - - - ;
    RAM_ADC_L:  .byte   1           ; Измеренное значение VOLT, младший байт
    RAM_ADC_H:  .byte   1           ; Измеренное значение VOLT, старший байт
BegBCDV:
    RAM_VLT_0:  .byte   1           ; Упакованный BCD VOLT, 1 байт
    RAM_VLT_1:  .byte   1           ; Упакованный BCD VOLT, 2 байт
EndBCDV:
.EQU	SizeBCDV = EndBCDV - BegBCDV
    RAM_UnBCDV: .byte   SizeBCDV*2  ; Неупакованные BCD VOLT
    RAM_SmblsV: .byte   BufSize     ; Коды цифр VOLT (symbols)

  Настроим ADC в разделе инициализации, т.к. опорное U=2,56V будет соответствовать числу 1023 (10 разрядов), максимальное измеряемое U при этом примем 10,23V. Это упростит расчеты.

;------ Настроить АЦП ----------------------------------------------------------;
; вход PIN_VOLT, внутреннее опорное 2,56V
    OUTI    ADMUX, PIN_VOLT|(1<<REFS1)|(1<<REFS0)
; включить АЦП, прескалер 64
    OUTI    ADCSRA, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)

  Канал измерения напряжения не требует точной временной привязки, запускать измерения будем из главного цикла, из подпрограммы сканирования клавиатуры. Каналы измерения частоты и напряжения будут работать постоянно и асинхронно, периодичность измерения частоты 1 Гц, напряжения ~ 3 Гц.

;== Опрос клавиатуры, MRK_Key=1 ================================================;
    SBIS  BITx1, MRK_Key            ; если нет  флага начала обработки, на выход
    RJMP  locMP_Kb                  ;
//
    ; управление битом порта PIN_LCLK
    IN    Temp, PortB               ;
    LDI   Cycler, (1<<PIN_LCLK)     ;
    EOR   Temp, Cycler              ; инвертируем бит PIN_LCLK
    OUT   PortB, Temp               ;
//
    ; обработка нажатия кнопки PIN_BTN
    SBIC  PIND, PIN_BTN             ; Если кнопка нажата, пропустить след. команду
    RJMP  CK_NoKey                  ;
    CPI   KeyPress, NUM_CKEY        ; Было NUM_CKEY опросов?
    BRSH  CK_Exit                   ;
    INC   KeyPress                  ; Если не было, инкремент регистра опросов
    RJMP  CK_Exit                   ;
CK_NoKey:
    CPI   KeyPress, NUM_CKEY        ; Было NUM_CKEY опросов?
    BRLO  CK_Stop                   ;
    ; Есть нажатие и отпускание
    IN    Temp, BITx1               ;
    LDI   Cycler, (1<<MRK_Btn)      ;
    EOR   Temp, Cycler              ; инвертируем флаг/триггер MRK_Btn
    OUT   BITx1, Temp               ;
CK_Stop:
    CLR   KeyPress                  ; очистка счетчика опросов
CK_Exit:
//
    ; запуск замера вольтметра
    SBIS  BITx2, MRK_VOLT           ;
    RJMP  NoADC                     ;
    DEC   TimerADC                  ;
    BRNE  NoADC                     ;
    LDIL  TimerADC, CountADC        ; периодичность замера вольтметра
    SBI   ADCSRA, ADSC              ; запуск АЦП 
    SBI   BITx2, MRK_eADC           ; установить флаг следующего шага
NoADC:
//
    CBI   BITx1, MRK_Key            ; сбросить флаг текущего шага
locMP_Kb:

  Одновременно отобразить результат двух разных измерений на однострочном дисплее проблемматично, выводится или одно, или другое. Для выбора отображаемого измерения в программе использована кнопка и флаг/триггер MRK_Btn меняющий свое состояние после нажатия и отпускания кнопки. При нажатии срабатывает счетчик антидребезга, при отпускании перебрасывается флаг. Кроме обработчика состояния кнопки в подпрограмме обработчика клавиатуры находится код инвертирующий состояния пина дисплея LCLK и код запускающий измерение вольтметра. Вольтметр запускается каждый CountADC (10) вызов обработчика. Трех измерений в секунду вполне достаточно.
Код подпрограммы, ждущий окончания цикла измерения ADC и переносящей измеренное значение в буфер:

;== Перенос замера ADC в ОЗУ, MRK_eADC = 1 =====================================;
    SBIS  BITx2, MRK_eADC           ; если нет  флага начала обработки, на выход
    RJMP  locMP_ADC                 ;
//
    SBIC  ADCSRA, ADSC              ; проверка готовности АЦП
    RJMP  locMP_ADC                 ; пропустить обработку
    IN    Temp, ADCL                ; перенос измерений в ОЗУ
    STS   RAM_ADC_L, Temp           ; -//-
    IN    Temp, ADCH                ; перенос измерений в ОЗУ
    STS   RAM_ADC_H, Temp           ; -//-
//
    SBI   BITx2, MRK_BCDV           ; установить флаг следующего шага
    CBI   BITx2, MRK_eADC           ; сбросить флаг текущего шага 
locMP_ADC:

  Код по возможности используется повторно, ведь требуемые операции аналогичны. Так подпрограмма BINtoPackBCD выделена в отдельную подпрограмму, ведь вызывается она из обоих каналов. Кроме того, так проще обеспечить обработку данных разной длины и по разным указателям.

;--- BINtoPackBCD, код на основе аппноты AVR204 --------------------------------;
.DEF    tBCD0       = R2            ; BCD value digits 1 and 0 
.DEF    tBCD1       = R3            ; BCD value digits 3 and 2 
.DEF    tBCD2       = R4            ; BCD value digits 4 and 5 
.DEF    tBCD3       = R5            ; BCD value digits 6 and 7
.DEF    FxBUF_L     = R20           ; Младший байт частоты (рабочий)
.DEF    FxBUF_M     = R21           ; Средний байт частоты (рабочий)
.DEF    FxBUF_H     = R22           ; Старший байт частоты (рабочий)
;
BINtoPackBCD:
    CLR   ZH                        ; очистить старший байт индексного регистра 
btpBCD1:
    LSL   FxBUF_L                   ; сдвинуть входное значение влево
    ROL   FxBUF_M                   ; -//-
    ROL   FxBUF_H                   ; -//-
    ROL   tBCD0                     ; и вдвинуть его в
    ROL   tBCD1                     ; результат справа
    ROL   tBCD2                     ; через флаг С
    ROL   tBCD3                     ; -//-
    DEC   Cycler                    ; декрементировать счетчик цикла 
    BREQ  btpBCD3                   ; выход при нуле 
    LDI   r30,AdBCD3+1              ; в Z адрес старшего регистра результата + 1 
btpBCD2: 
    LD    Temp,-Z                   ; загрузить старший регистр результата (предекремент)
    SUBI  Temp,-$03                 ; сложить с 0x03 
    SBRC  Temp,3                    ; если бит 3 очищен, пропустить след. команду 
    ST    Z,Temp                    ; сохранить в регистре результата
    LD    Temp,Z                    ; загрузить старший регистр результата
    SUBI  Temp,-$30                 ; сложить с 0x30 
    SBRC  Temp,7                    ; если бит 7 очищен, пропустить след. команду
    ST    Z,Temp                    ; сохранить в регистре результата
    CPI   ZL,AdBCD0                 ; конец достигнут?
    BRNE  btpBCD2                   ; если нет, повторить
    RJMP  btpBCD1                   ; продолжить цикл 
btpBCD3:
    RET                             ;
;--- end BINtoPackBCD ----------------------------------------------------------;
;
;== Преобразование FREQ PackBCD <- BIN, MRK_BCDF = 1 ===========================;
;
    SBIS  BITx1, MRK_BCDF           ; если нет  флага начала обработки, на выход
    RJMP  locMPF_00                 ;
//
    IN    Temp, MCUCSR              ; Проверка на холодный старт
    ANDI  Temp, (1<<EXTRF)|(1<<PORF);
    BREQ  locBCD0                   ; Если нет - выход
    SBI   BITx1, MRK_Beg            ; установить флаг начальной загрузки
    CLR   Temp                      ;
    OUT   MCUCSR, Temp              ;
    RJMP  locBCD4                   ; отбрасывание первого измерения
locBCD0:
    LDS   FxBUF_H, RAM_Val_H        ; загрузка измеренного значения
    LDS   FxBUF_M, RAM_Val_M        ; -//-
    LDS   FxBUF_L, RAM_Val_L        ; -//-
    CLR   tBCD3                     ; очистить регистры результата
    CLR   tBCD2                     ; -//-
    CLR   tBCD1                     ; -//-
    CLR   tBCD0                     ; -//- 
    LDI   Cycler,24                 ; Кол-во итераций (24 бита) 
    RCALL BINtoPackBCD              ;
    STS   RAM_BCD_0, tBCD0          ; сохранение упакованного FREQ BCD
    STS   RAM_BCD_1, tBCD1          ; -//-
    STS   RAM_BCD_2, tBCD2          ; -//-
    STS   RAM_BCD_3, tBCD3          ; -//-
//
    SBI   BITx1, MRK_B2BF           ; установить флаг следующего шага
locBCD4:
    CBI   BITx1, MRK_BCDF           ; сбросить флаг текущего шага 
locMPF_00: ;====================================================================;
;
;== Преобразование VOLT PackBCD <- BIN, MRK_BCDV = 1 ===========================;
;
    SBIS  BITx2, MRK_BCDV           ; если нет  флага начала обработки, на выход
    RJMP  locMPV_00                 ;
//
    LDS   FxBUF_H, RAM_ADC_H        ; загрузка измеренного значения
    LDS   FxBUF_M, RAM_ADC_L        ; -//-
    CLR   tBCD1                     ; очистить регистры результата
    CLR   tBCD0                     ; -//- 
    LDI   Cycler,16                 ; Кол-во итераций (16 бит) 
    RCALL BINtoPackBCD              ;
    STS   RAM_VLT_0, tBCD0          ; сохранение упакованного VOLT BCD
    STS   RAM_VLT_1, tBCD1          ; -//-
//
    SBI   BITx2, MRK_B2BV           ; установить флаг следующего шага
    CBI   BITx2, MRK_BCDV           ; сбросить флаг текущего шага 
locMPV_00: ;====================================================================;
;
.UNDEF  tBCD0                       ; R2  
.UNDEF  tBCD1                       ; R3 
.UNDEF  tBCD2                       ; R4 
.UNDEF  tBCD3                       ; R5 
.UNDEF  FxBUF_L                     ; R20
.UNDEF  FxBUF_M                     ; R21
.UNDEF  FxBUF_H                     ; R22

Аналогично сделано и для подпрограммы PackBCDtoUnpackBCD.

;---- PackBCDtoUnpackBCD -------------------------------------------------------;
; На входе:
; X - адрес буфера неупакованных BCD
; Y - адрес буфера упакованных BCD
; Cycler - кол-во байт упакованных BCD
;-------------------------------------------------------------------------------;
.DEF    Mask_4      = R20           ; маска ниббла
.DEF    PackBCD     = R21           ; упакованный BCD
.DEF    CopyBCD     = R22           ; копия -//-
;
PackBCDtoUnpackBCD:
    LDI   Mask_4, 0b00001111        ; маска ниббла
locP2U:
    LD    PackBCD, Y+               ; чтение упакованного BCD
    MOV   CopyBCD, PackBCD          ; копия
    AND   PackBCD, Mask_4           ; младший ниббл
    ST    X+, PackBCD               ; в буфер неупакованных BCD
    SWAP  CopyBCD                   ; перевернем копию
    AND   CopyBCD, Mask_4           ; старший ниббл
    ST    X+, CopyBCD               ; в буфер неупакованных BCD
    DEC   Cycler                    ;
    BRNE  locP2U                    ;
    RET                             ;
;
.UNDEF    Mask_4                    ; R20
.UNDEF    PackBCD                   ; R21
.UNDEF    CopyBCD                   ; R22
;---- end PackBCDtoUnpackBCD ---------------------------------------------------;
;
;== Преобразование FREQ UnpackBCD <- PackBCD, MRK_B2BF = 1 =====================;
    SBIS  BITx1, MRK_B2BF           ; если нет  флага начала обработки, на выход
    RJMP  locMPF_01                 ;
//
    LDIDX X, RAM_UnBCDF             ; адрес буфера неупакованных FREQ BCD
    LDIDX Y, RAM_BCD_0              ; адрес буфера упакованных FREQ BCD
    LDI   Cycler, SizeBCDF          ; кол-во байт упакованных FREQ BCD
    RCALL PackBCDtoUnpackBCD        ; распаковка
//
    SBI   BITx2, MRK_VOLT           ; разрешить вольтметр
    SBI   BITx1, MRK_SymF           ; установить флаг следующего шага
    CBI   BITx1, MRK_B2BF           ; сбросить флаг текущего шага 
locMPF_01: ;=====================================================================;
;
;== Преобразование VOLT UnpackBCD <- PackBCD, MRK_B2BV = 1 =====================;
    SBIS  BITx2, MRK_B2BV           ; если нет  флага начала обработки, на выход
    RJMP  locMPV_01                 ;
//
    LDIDX X, RAM_UnBCDV             ; адрес буфера неупакованных VOLT BCD
    LDIDX Y, RAM_VLT_0              ; адрес буфера упакованных VOLT BCD
    LDI   Cycler, SizeBCDV          ; кол-во байт упакованных VOLT BCD 
    RCALL PackBCDtoUnpackBCD        ; распаковка
//
    SBI   BITx2, MRK_SymV           ; установить флаг следующего шага
    CBI   BITx2, MRK_B2BV           ; сбросить флаг текущего шага 
locMPV_01: ;=====================================================================;

  Коды подпрограмм UnpackBCDtoSymbolsCode для каналов FREQ и VOLT сделаны разными. Сделано это из-за различия алгоритмов гашения незначащего нуля. Если в канале FREQ ноль должен гаситься пока не останется младшая цифра, то в канале VOLT младшая цифра находится в третьем разряде, а первый/второй разряд — это дробная часть. Поэтому подпрограмма канала VOLT гасит ноль только в четвертом разряде и в третьем разряде вставляет десятичную точку.
  Кроме этого подпрограммы в самом старшем (девятом) разряде выставляют код канала. Для FREQ это 'F', для VOLT это 'U'. Выбор отображаемого канала также производится в этих подпрограммах. В зависимости от значения флага MRK_Btn разрешается дальнейший вывод результата на индикацию или канала FREQ, или канала VOLT.

;== Преобразование FREQ Symbols code <- UnpackBCD с гашением 0, MRK_SymF = 1 ===;
    SBIS	BITx1, MRK_SymF         ; если нет  флага начала обработки, на выход
    RJMP    locMPF_02               ;
//
    CLT                             ; очистить Т
    LDIDX X, RAM_UnBCDF+BufSize-1   ;
    LDIDX Y, RAM_SmblsF+BufSize     ; начинаем с верхних адресов
    LDI   Cycler, BufSize-1         ;
    LDI   Temp, CH_F                ; Загрузить символ частоты (F)
    ST    -Y, Temp                  ; сохранить код символа
locSm0:
    DEC   Cycler                    ; Z=1 - последняя цифра
    LDIZ  TBL_DIGITS                ; Загрузить адрес таблицы кодов символов
    LD    Temp, -X                  ; загрузить неупакованный BCD 
    BRTS  locSm1                    ; если Т установлен
    BREQ  locSm1                    ; если последняя цифра
    TST   Temp                      ;
    BREQ  locSm2                    ; если ноль
    SET                             ; установить Т
locSm1:
    ADD   ZL, Temp                  ; Найти
    CLR   Temp                      ; нужный 
    ADC   ZH, Temp                  ; символ
    LPM   Temp, Z                   ; загрузить код символа
locSm2:
    ST    -Y, Temp                  ; сохранить код символа
    TST   Cycler                    ;
    BRNE  locSm0                    ;
//
    SBIS  BITx1, MRK_Btn            ; если 1 (VOLT), пропустим след. команду
    SBI   BITx1, MRK_RdyF           ; установить флаг следующего шага
    CBI  BITx1, MRK_SymF            ; сбросить флаг текущего шага 
locMPF_02: ;=====================================================================;
;
;== Преобразование VOLT Symbols code <- UnpackBCD, гашение 0, MRK_SymV = 1 ======;
    SBIS  BITx2, MRK_SymV           ; если нет  флага начала обработки, на выход
    RJMP  locMPV_02                 ;
//
    CLT                             ; очистить Т
    LDIDX X, RAM_UnBCDV+SizeBCDV*2  ; начинаем с верхних адресов
    LDIDX Y, RAM_SmblsV+SizeBCDV*2  ;
    LDI   Cycler, SizeBCDV*2        ;
locSm4:
    LDIZ  TBL_DIGITS                ; Загрузить адрес таблицы кодов символов
    LD    Temp, -X                  ; загрузить неупакованный BCD 
    BRTS  locSm5                    ; если Т установлен
    TST   Temp                      ;
    BRNE  locSm5                    ; если не ноль
    LDI   Temp, 0                   ; символ гашения
    RJMP  locSm6                    ;
locSm5:
    ADD   ZL, Temp                  ; Найти
    CLR   Temp                      ; нужный 
    ADC   ZH, Temp                  ; символ
    LPM   Temp, Z                   ; загрузить код символа
locSm6:
    SET                             ; установить Т
    CPI   Cycler, SizeBCDV+1        ; 
    BRNE  locSm7                    ;
    ORI   Temp, Seg_H               ; добавим точку 
locSm7:
    ST    -Y, Temp                  ; сохранить код символа
    DEC   Cycler                    ;
    BRNE  locSm4                    ;
;----- гашение ---------------------;
    LDIDX Y, RAM_SmblsV+SizeBCDV*2  ;
    LDI   Cycler, BufSize-SizeBCDV*2-1;
    LDI   Temp, 0                   ; символ гашения
locSm8:
    ST    Y+, Temp                  ; сохранить код символа
    DEC   Cycler                    ;
    BRNE  locSm8                    ;
;-----------------------------------;
    LDI   Temp, CH_U                ; Загрузить символ напряжения (U)
    ST    Y+, Temp                  ; сохранить код символа
//
    SBIC  BITx1, MRK_Btn            ; если 0 (FREQ), пропустим след. команду
    SBI   BITx2, MRK_RdyV           ; установить флаг следующего шага
    CBI   BITx2, MRK_SymV           ; сбросить флаг текущего шага 
locMPV_02: ;=====================================================================;

  Таким образом даже если данный канал в текущее время не отображается, измерения и расчеты в нем производятся всегда. Для данного однострочного индикатора такое построение измерительного тракта возможно излишне, но для многострочного (например на контроллере HD44780) позволяет вывести результаты измерений обоих каналов одновременно.

;== Вывод результата на индикацию FREQ , MRK_RdyF = 1 ==========================;
    SBIS  BITx1, MRK_RdyF           ; если нет  флага начала обработки, на выход
    RJMP  locMPF_03                 ;
    OUTI  TCCR0, (1<<CS00)          ; перезапустить таймер 0, прескалер 0
    LDIDX X, RAM_SmblsF             ; указатель на буфер символов FREQ
    RCALL SetDisplayData            ;
    SBI   BITx1, MRK_Disp           ; разрешить вывод битов по SPI
    CBI   BITx1, MRK_RdyF           ; сбросить флаг текущего шага 
locMPF_03: ;====================================================================;
;
;== Вывод результата на индикацию VOLT , MRK_RdyV = 1 ==========================;
    SBIS  BITx2, MRK_RdyV           ; если нет  флага начала обработки, на выход
    RJMP  locMPV_03                 ;
    OUTI  TCCR0, (1<<CS00)          ; перезапустить таймер 0, прескалер 0
    LDIDX X, RAM_SmblsV             ; указатель на буфер символов VOLT
    RCALL SetDisplayData            ;
    SBI   BITx1, MRK_Disp           ; разрешить вывод битов по SPI
    CBI   BITx2, MRK_RdyV           ; сбросить флаг текущего шага 
locMPV_03: ;====================================================================;

Проверим что у нас с памятью:



  Вариант программы для сдвиговых регистров 74НС595 полностью аналогичен, за исключением возможности управлять как индикаторами с ОК, так и индикаторами с ОА. Вариант для дисплеев на MAX7219 и HD44780 в построении измерительной части аналогичны описанной для ML1001. Отличия связаны с различиями управления контроллеров и были рассмотрены во 2 части.

Дисплей с контроллером MAX7219 + милливольтметр.

  Отличия в программе для MAX7219 от рассмотренной во 2 части, кроме добавления кода канала измерения напряжения, минимальны. Они связаны с тем, что ранее были включены дешифраторы на всех восьми разрядах дисплея. Это мешает отобразить в старшем разряде символ отображаемого канала ('F' или 'U'). Поэтому дешифратор в старшем разряде включать не нужно. Проектировщики посчитали старшим самый правый бит в байте, хотя в BIN/BCD арифметике правый бит считается младшим.

;--- Константы MAX7219 ---------------------------------------------------------;
; . . . . . . . . . . . . . . . . . .
.EQU    EnDecode    = 0b11111110    ; Дешифраторы 1..7 включены, 8 отключен, адрес 9
; . . . . . . . . . . . . . . . . . .

  И соответственно в подпрограмме PackBCDtoUnpackBCD необходимо будет положить в старший разряд буфера соответствующий символ канала измерения.
Занимаемая память:



Дисплей с контроллером HD44780 + милливольтметр.

  В первоначальном варианте контроллер HD44780 подключен к порту С, на котором находятся входы ADC. Поэтому он был перенесен на порт D, причем не прямо, а с изменением порядка подключения. Удобный случай показать какие изменения в программе для этого необходимы. Их немного. Т.к. коды команд контроллера привязаны к символьным обозначениям выводов, при переносе на другие ножки их корректировка не потребовалась. Потребовалось только немного изменить код подпрограммы LCD_SendTetraByte для отправки данных. Если вначале ниббл данных был в середине байта, теперь он переместился в младшую половину байта.

LCD_SendTetraByte:
    MOV   Tmp, Temp                 ;
    SWAP  Temp                      ; старшую половину в младшую
    ORI   Temp, LCD_SET             ; сформировать полный байт
    LDIW  HDelay, LDelay, Del37mkc  ; Задержка 37 mkc
    OUT   PORTD, Temp               ;
    RCALL LCD_Waiting               ; Задержка
    ORI   Tmp, LCD_SET              ; сформировать полный байт
    LDIW  HDelay, LDelay, Del37mkc  ; Задержка 37 mkc
    OUT   PORTD, Tmp                ;
    RCALL LCD_Waiting               ; Задержка
    OUTI  PORTD, LCD_BUSY           ;
    RET                             ;

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

Занимаемая память:



  Сюда входит и код определения и программирования двух символов пользователя. Этот код в программе не используется и приведен для примера. Если его убрать:



  В любом варианте программы осталось свободно ~90% памяти программ и более 95% памяти данных. Остались и не разу не использованные регистры (R6-R15, R23-R25). Хватит, чтобы при желании реализовать любые свои хотелки.

В приложенном архиве FreqVolt.zip проекты AVR Studio и Proteus. Подробнее файл Readme внутри.
  • 0
  • 17 января 2018, 20:21
  • anakost
  • 1
Файлы в топике: FreqVolt.zip

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

RSS свернуть / развернуть
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.