;
; 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 ; Состояние кнопок
TEMP_L: .BYTE 1 ; Младший байт температуры
TEMP_H: .BYTE 1 ; Старший байт температуры
TEMP_F: .BYTE 1 ; Дробная часть
SETTING_INT: .BYTE 1 ; Уставка: целая часть
SETTING_F: .BYTE 1 ; Уставка: дробная часть
SETTING_HYST: .BYTE 1 ; Гистерезис: отклонение от уставки
SETTING_MODE: .BYTE 1 ; Режим работы: '1' - нагрев; '0' - 'охлаждение'
DEVICE_FAMILY_CODE: .BYTE 1 ; Должно быть 0x10 для DS18B20
;//
//
; **** СЕГМЕНТ КОДА **********************************************
.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
; display_off
; in r17, SW_PIN
; clr r18
; ldi r18, (1<
//
; **** ДИНАМИЧЕСКАЯ ИНДИКАЦИЯ ************************************
TIMER0_COMPA_vect:
push r20
push r21
push r16
push r17
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
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
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
lds r19, DIGITS+2
or TEMP_REG_A, r19
breq PC+3
sbi PORTD, DIGIT_2_PIN
rjmp PC+2
cbi PORTD, DIGIT_2_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, TEMP_F
rcall DISPLAY_DECODER
rcall USI_TRANSMIT
_indicate_exit:
inc r20
sts CURRENT_DIGIT, r20
out SREG, r21
pop r17
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
cbi LED_ERR_PORT, LED_ERR_PIN
rcall DISPLAY_UPD_DIGITS
rcall TEMP_UPD
outi r16, TIMSK, (1<
; **** ПОДПРОГРАММЫ **********************************************
.INCLUDE "div16u.asm"
TEMP_COMPARSION:
push r16
push r17
push r18
push r19
push r20
push r21
push r22
lds r16, TEMP_L
lds r17, TEMP_H // всегда 0?
lds r18, TEMP_F
; lds r19, SETTING_INT
; lds r20, SETTING_F
ldi r19, 28 ; min
ldi r20, 32 ; max
lds r21, SETTING_MODE ; mode
brts PC+2
rjmp PC+2
neg r16
tst r21
brne _HEATING_MODE
rjmp _COMPARSION_EXIT
_HEATING_MODE:
cp r19, r16
brge _HEATING_ON ; TEMP <= MIN
rjmp _HEATING_OFF
_HEATING_ON:
relay_on
rjmp _COMPARSION_EXIT
_HEATING_OFF:
cp r16, r20
brge PC+2 ; TEMP >= MAX
rjmp _COMPARSION_EXIT
relay_off
_COMPARSION_EXIT:
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
ret
//
TEMP_UPD:
push r17
rcall TEMP_CONV
DELAY24 500000
DELAY24 500000
rcall TEMP_RD
; обновляем данные в ячейках
lds r17, TEMP_L
mov DISP_NUM_L, r17
lds r17, TEMP_H
mov DISP_NUM_H, r17
pop r17
ret
//
//
RD_F_CODE:
push r16
rcall OW_PRESENCE
ldi r16, DS18B20_CMD_READROM
mov OW_CMD_r, r16
rcall OW_SEND_BYTE
ldi XL, LOW(DEVICE_FAMILY_CODE)
ldi XH, HIGH(DEVICE_FAMILY_CODE)
rcall OW_RD_BYTE
pop r16
ret//
//
TEMP_RD:
push r16
push r17
push r18
push r19
push r20
; 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:
lds r19, TEMP_L
brtc PC+2
neg r19 ; переводим в беззнаковое число если минус
; далее происходит такое для получения дробной части:
; ((n<<3) + (n<<1))>>4 или (n*10)/16
mov r20, r19
andi r19, 0x0f
mov r20, r19
lsl r19
lsl r19
lsl r19
lsl r20
add r19, r20
lsr r19
lsr r19
lsr r19
lsr r19
; записываем данные
sts TEMP_L, r17
sts TEMP_H, r18
sts TEMP_F, r19
; sei
pop r20
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//
//
BEEP_SHORT:
sbi BUZZER_PORT, BUZZER_PIN
rcall DELAY
cbi BUZZER_PORT, BUZZER_PIN
ret
//
//
BEEP_LONG:
sbi BUZZER_PORT, BUZZER_PIN
rcall DELAY
rcall DELAY
rcall DELAY
rcall DELAY
cbi BUZZER_PORT, BUZZER_PIN
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_LOOP_16:
subi DELAY_8_r, 1
sbci DELAY_16_r, 0
brcc DELAY_LOOP_16
ret
//
//
DELAY_LOOP_24:
subi DELAY_8_r, 1
sbci DELAY_16_r, 0
sbci DELAY_24_r, 0
brcc DELAY_LOOP_24
ret
//
//
DELAY:
push r16
push r17
push r18
ldi r18, 6
_DELAY_0:
ldi r16, 255
_DELAY_1:
ldi r17, 255
_DELAY_2:
dec r17
brne _DELAY_2
dec r16
brne _DELAY_1
dec r18
brne _DELAY_0
pop r18
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"
;