; ; Project name: thermostat ; Description: Electronic thermostat on AVR Microcontroller ; Source code: https://github.com/sergeyyarkov/attiny2313a_thermostat ; Device: ATtiny2313A ; Device Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/doc8246.pdf ; Assembler: AVR macro assembler 2.2.7 ; Clock frequency: 8 MHz External Crystal Oscillator ; Fuses: lfuse: 0xCF, hfuse: 0x9F, efuse: 0xFF, lock: 0xFF ; ; Written by Sergey Yarkov 22.01.2023 .LIST .INCLUDE "definitions.asm" .INCLUDE "macros.asm" // ; **** СЕГМЕНТ ДАННЫХ ******************************************** .DSEG .ORG SRAM_START MCU_STATE: .BYTE 1 ; Текущее состояние МК SRAM_TEMP_1: .BYTE 2 ; Хранения временного 16-бит числа в ячейке DIGITS: .BYTE 4 ; Ячейки, где хранятся символы, для вывода на индикатор CURRENT_DIGIT: .BYTE 1 ; Номер разряда индикатора, который сейчас горит SW_FLAGS: .BYTE 1 ; Состояние кнопок HYSTERESIS: .BYTE 1 ; Отклонение температуры от уставки TEMP_L: .BYTE 1 TEMP_H: .BYTE 1 ;// // ; **** СЕГМЕНТ КОДА ********************************************** .CSEG // .ORG 0x00 rjmp RESET_vect .ORG 0x000B ; rjmp PCINT0_vect reti .ORG 0x000D rjmp TIMER0_COMPA_vect // // PCINT0_vect: push r16 push r17 push r18 push r19 in r16, SREG ; in r17, SW_PIN ; clr r18 ; ldi r18, (1< // ; **** ДИНАМИЧЕСКАЯ ИНДИКАЦИЯ ************************************ TIMER0_COMPA_vect: push r20 push r21 push r16 in r21, SREG lds r20, CURRENT_DIGIT cpi r20, 5 brge _CLR_CURRENT_DIGIT ; сброс активного разряда если >= 5 rjmp _indicate_1 _CLR_CURRENT_DIGIT: ; сброс текущего активного разряда в ноль clr r20 sts CURRENT_DIGIT, r20 _indicate_1: cpi r20, 0 brne _indicate_2 cbi PORTD, DIGIT_2_PIN cbi PORTD, DIGIT_3_PIN cbi PORTD, DIGIT_4_PIN ; lds TEMP_REG_A, DIGITS+2 brts PC+2 rjmp PC+3 ldi TEMP_REG_A, 11 ; // - rjmp PC+3 lds TEMP_REG_A, DIGITS+2 tst TEMP_REG_A breq PC+3 sbi PORTD, DIGIT_1_PIN rjmp PC+2 cbi PORTD, DIGIT_1_PIN ; lds TEMP_REG_A, DIGITS+2 rcall DISPLAY_DECODER rcall USI_TRANSMIT _indicate_2: cpi r20, 1 brne _indicate_3 cbi PORTD, DIGIT_1_PIN cbi PORTD, DIGIT_3_PIN cbi PORTD, DIGIT_4_PIN ; lds TEMP_REG_A, DIGITS+1 ; tst TEMP_REG_A ; breq PC+3 sbi PORTD, DIGIT_2_PIN ; rjmp PC+2 ; cbi PORTD, DIGIT_1_PIN lds TEMP_REG_A, DIGITS+1 rcall DISPLAY_DECODER rcall USI_TRANSMIT _indicate_3: cpi r20, 2 brne _indicate_4 cbi PORTD, DIGIT_1_PIN cbi PORTD, DIGIT_2_PIN cbi PORTD, DIGIT_4_PIN sbi PORTD, DIGIT_3_PIN lds TEMP_REG_A, DIGITS rcall DISPLAY_DECODER ; зажигаем точку mov r16, r0 cbr r16, (1<<7) mov r0, r16 rcall USI_TRANSMIT _indicate_4: cpi r20, 3 brne _indicate_exit cbi PORTD, DIGIT_1_PIN cbi PORTD, DIGIT_2_PIN cbi PORTD, DIGIT_3_PIN sbi PORTD, DIGIT_4_PIN ; lds TEMP_REG_A, DIGITS ldi TEMP_REG_A, 0 rcall DISPLAY_DECODER rcall USI_TRANSMIT _indicate_exit: inc r20 sts CURRENT_DIGIT, r20 out SREG, r21 pop r16 pop r21 pop r20 reti ;// ; **** СТАРТ ПРОГРАММЫ ******************************************* RESET_vect: ldi TEMP_REG_A, LOW(RAMEND) out SPL, TEMP_REG_A // ; **** ПРОЦЕСС ИНИЦИАЛИЗАЦИИ МК ********************************** MCU_INIT: ; **** ИНИЦИАЛИЗАЦИЯ ПИНОВ ************************************* outi r16, DDRD, (1< // ; **** ГЛАВНЫЙ ЦИКЛ ********************************************** LOOP: ; rcall DISPLAY_UPD_DIGITS lds r16, MCU_STATE ; получаем текущее состояние МК _STATE_DEFAULT: cpi r16, MCU_STATE_DEFAULT brne _STATE_PROGRAM rcall DISPLAY_UPD_DIGITS sbrc OWFR, OWPRF sbi PORTD, PD6 sbrs OWFR, OWPRF cbi PORTD, PD6 rcall TEMP_CONV rcall DEBOUNCE_SW rcall DEBOUNCE_SW rcall TEMP_RD lds r17, TEMP_L mov DISP_NUM_L, r17 lds r17, TEMP_H mov DISP_NUM_H, r17 _STATE_PROGRAM: cpi r16, MCU_STATE_PROGRAM brne _STATE_ERROR rcall DISPLAY_UPD_DIGITS rcall SW_CHECK_PROCESS _STATE_ERROR: cpi r16, MCU_STATE_ERROR brne LOOP ldi r17, 11 sts DIGITS, r17 sts DIGITS+1, r17 sts DIGITS+2, r17 sts DIGITS+3, r17 nop rjmp LOOP // ; **** ПОДПРОГРАММЫ ********************************************** .INCLUDE "div16u.asm" DELAY_LOOP_16: subi DELAY_8_r, 1 sbci DELAY_16_r, 0 brcc DELAY_LOOP_16 ret // TEMP_RD: push r16 push r17 push r18 push r19 ; cli rcall OW_PRESENCE ldi r16, DS18B20_CMD_SKIPROM mov OW_CMD_r, r16 rcall OW_SEND_BYTE ldi r16, DS18B20_CMD_RSCRATCHPAD mov OW_CMD_r, r16 rcall OW_SEND_BYTE ldi XL, LOW(TEMP_L) ldi XH, HIGH(TEMP_L) rcall OW_RD_BYTE ldi XL, LOW(TEMP_H) ldi XH, HIGH(TEMP_H) rcall OW_RD_BYTE lds r17, TEMP_L lds r18, TEMP_H push r18 cbr r18, (1<<0) | (1<<1) | (1<<2) ; убираем ненужные биты на время (интересуют последних битов в TEMP_H) cpi r18, 0xf8 ; проверяется является ли число минусовой breq _TEMP_RD_TO_UNSIGNED ; если да то конвертируем в беззнаковое число clt rjmp _TEMP_RD_CONTINUE ; если нет то преобразуем значение АЦП в температуру (делим на 16) _TEMP_RD_TO_UNSIGNED: pop r18 set com r17 com r18 ldi r19, 1 add r17, r19 ldi r19, 0 adc r18, r19 rjmp PC+2 _TEMP_RD_CONTINUE: ; Температура = Число с АЦП / 16 (сдвиг вправо 4) pop r18 lsr r18 ror r17 lsr r18 ror r17 lsr r18 ror r17 lsr r18 ror r17 _TEMP_RD_END: sts TEMP_L, r17 sts TEMP_H, r18 ; sei pop r19 pop r18 pop r17 pop r16 ret ; TEMP_CONV: push r16 ; cli rcall OW_PRESENCE ldi r16, DS18B20_CMD_SKIPROM mov OW_CMD_r, r16 rcall OW_SEND_BYTE ldi r16, DS18B20_CMD_CONVERTTEMP mov OW_CMD_r, r16 rcall OW_SEND_BYTE ; sei pop r16 ret // // ; **** ОТПРАВКА БАЙТА В СДВИГОВЫЙ РЕГИСТР ************************* USI_TRANSMIT: push r16 out USIDR, r0 ; Байт для отправки всегда находится в регистре r0. Помещаем данные в регистр USIDR. ; Enable USI Overflow Interrupt Flag (will be 0 if transfer is not compeleted) ldi TEMP_REG_A, (1< USI Wire Mode ; USICS1 <--------------> USI Clock Source Select ; USICLK <--------------> USI Clock Strobe ; USITC <--------------> USI Toggle Clock (Enable clock generation) ldi TEMP_REG_A, (1< // SW_CHECK_PROCESS: push r16 rcall DEBOUNCE_SW sbis SW_PIN, SW_PLUS_PIN adiw DISP_NUM_L, 1 sbis SW_PIN, SW_MINUS_PIN sbiw DISP_NUM_L, 1 sbis SW_PIN, SW_SET_PIN rjmp _INTO_DEFAULT_STATE rjmp _SW_CHECK_PROCESS_END _INTO_DEFAULT_STATE: ldi r16, MCU_STATE_DEFAULT sts MCU_STATE, r16 _SW_CHECK_PROCESS_END: pop r16 ret // // // ; **** ОПРОС ПРИСУТСТВИЯ УСТРОЙСТВА ****************************** OW_PRESENCE: cli ow_pull DELAY16 480 ow_release DELAY16 70 rcall OW_CHECK_PRESENCE DELAY16 410 sei ret // // ; **** ПРОВЕРКА НАЛИЧИЯ УСТРОЙСТВА ******************************* ; Если подчиненное устройство притянет шину, то устанавливаем флаг OWPRF в единицу в регистре флагов ; OW_CHECK_PRESENCE: sbis OW_PIN, OW_LINE sbr OWFR, (1< // OW_SEND_BYTE: push r16 push r17 mov r16, OW_CMD_r ldi r17, 8 _OW_SEND_BYTE_LOOP: lsr r16 brcc _OW_SEND_0 brcs _OW_SEND_1 _OW_SEND_0: cli ow_pull DELAY16 60 ow_release DELAY16 10 sei rjmp _OW_SEND_BYTE_END _OW_SEND_1: cli ow_pull DELAY16 6 ow_release DELAY16 64 sei _OW_SEND_BYTE_END: dec r17 brne _OW_SEND_BYTE_LOOP pop r17 pop r16 ret // // OW_RD_BYTE: push r16 push r17 clr r16 ldi r17, 8 cli _OW_RD_BYTE_LP: lsr r16 ow_pull DELAY16 6 ow_release DELAY16 9 sbic OW_PIN, OW_LINE sbr r16, (1<<7) DELAY16 55 dec r17 brne _OW_RD_BYTE_LP st X, r16 sei pop r17 pop r16 ret // // // ; **** ПОЛУЧЕНИЕ ЦИФР ИЗ 16-ТИ БИТНОГО ЧИСЛА ********************* ; Описание: Перемещает цифры числа в соответствующие ячейки памяти в SRAM ; путем деления этого числа несколько раз DISPLAY_UPD_DIGITS: cli push r21 ldi r21, 4 ; (4 раза делим) т.к индикатор четырех разрядный ; инициализация указателя (за каждый проход цикла будет инкрементироваться) ldi XL, LOW(DIGITS) ldi XH, HIGH(DIGITS) .equ dividend = SRAM_TEMP_1 ; число которе будем делить .equ divisor = 10 ; на что делим ; загружаем число которое хотим поделить в адрес SRAM делимого sts dividend, DISP_NUM_L sts dividend+1, DISP_NUM_H ; четыре раза производим деление для получения остатков DIV_LOOP: ; заполняем нужные регистры lds dd16uL, dividend lds dd16uH, dividend+1 ldi dv16uL, LOW(divisor) ldi dv16uH, HIGH(divisor) rcall div16u ; делим st X+, drem16uL ; сохраняем остаток в ячейку по указателю и увеличиваем его ; обновляем делимое sts dividend, dres16uL sts dividend+1, dres16uH dec r21 ; декрементируем счетчик цикла brne DIV_LOOP ; делим еще раз если не 0 pop r21 sei ret // // ; **** ЗАГРУЖАЕТ НУЖНЫЙ АДРЕС СИМВОЛА В R0 *********************** DISPLAY_DECODER: push r16 push r17 ldi ZL, LOW(2*DISPLAY_SYMBOLS) ldi ZH, HIGH(2*DISPLAY_SYMBOLS) clr TEMP_REG_B add ZL, TEMP_REG_A adc ZH, TEMP_REG_B lpm r0, Z pop r17 pop r16 ret// // DEBOUNCE_SW: push r16 push r17 push r18 ldi r18, 10 _loop_2: ldi r17, 255 _loop_0: ldi r16, 255 _dec_0: dec r16 brne _dec_0 _loop_1: dec r17 brne _loop_0 _loop_3: dec r18 brne _loop_2 pop r18 pop r17 pop r16 ret// // DELAY: push r16 push r17 ldi r16, 255 _DELAY_1: ldi r17, 255 _DELAY_2: dec r17 nop nop nop brne _DELAY_2 dec r16 brne _DELAY_1 pop r17 pop r16 ret // // DISPLAY_SYMBOLS: ; HGFEDCBA HGFEDCBA .DB 0b11000000, 0b11111001 ; 0, 1 .DB 0b10100100, 0b10110000 ; 2, 3 .DB 0b10011001, 0b10010010 ; 4, 5 .DB 0b10000010, 0b11111000 ; 6, 7 .DB 0b10000000, 0b10010000 ; 8, 9 .DB 0b10011100, 0b10111111 ; °, -// // // ; **** СЕГМЕНТ EEPROM ******************************************** ; .ESEG ; INFO: .DB "AVR Thermostat. Written by Sergey Yarkov 22.01.2023" ;