====Аннотация====
В данном программном документе приведен текст программы «telemetry_rev1-1», предназначенной для опроса телеметрических датчиков, подключенных к микроконтроллеру Atmel Atmega328P, и отправки полученных данных по последовательному порту на бортовую ЭВМ. Текст программы реализован в виде символической записи на языке ассемблера AVR. Компилятором является консольная программа для UNIX-подобных операционных систем - avra.
Основной функцией программы telemetry_rev1-1 является опрос и отправка показаний следующих датчиков:
- по протоколу I2C:
- BMP180 - атмосферное давление и температура окружающей среды;
- BH1750 - освещенность;
- по протоколу 1-Wire:
- DS18B20 - температура элементной базы робота;
- аналоговые, с предварительной обработкой АЦП:
- ACS712-5A / MAX471 - потребляемый ток;
- Датчики напряжения собственного изготовления.
В процессе сбора данных происходит их перевод в двоично-десятичный код и отправка по USB-to-Serial протоколу с сохранением в памяти бортовой ЭВМ.
Программа состоит из основной программы, подключаемых модулей и файла объявления имен регистров:
*telemetry_rev1-1.asm - основная программа;
*1wire.asm - работа с датчиками по 1-Wire протоколу;
*hextobcd.asm - перевод чисел из шестнадцатеричной системы счисления в двоично-десятичный код;
*iterrupts.asm - адреса прерываний микроконтроллера;
*macr.asm - макросы, используемые в основной программе;
*twi_lib.asm - работа с датчиками по I2C протоколу;
*m328Pdef.inc - объявление имен регистров и ячеек памяти.
====Текст программы telemetry_rev1-1.asm====
;----------------------------------------------------
; Program : telemetry_rev1-1
; Compiler : AVRA
; Chip type : ATmega328P
; System Clock : 16 MHz
; Date : 18.01.2017
;--------------------------------------------------------------------------------------------------------------
;Подключение библиотек и резервация места под данные
.include "m328Pdef.inc "
.dseg
adc_data:
.byte8
Trm:; Ячейки ОЗУ под показания датчиков DS18B20
.byte14
bmp_temp:; Ячейки ОЗУ под показания темп. датчика BMP180
.byte2
bmp_pres:; Ячейки ОЗУ под показания давления датчика BMP180
.byte2
bh_lux:
.byte2
.cseg
.include "iterrupts.asm"; Библиотека векторов прерываний
.include "macr.asm"; Библиотека макросов
.include "twi_lib.asm"; Библиотека работы шины TWI
.include "hextobcd.asm"; Библиотека перевода чисел в неупакованный 2-10 код
.include "1wire.asm"; Библиотека 1-wire устройств
;--------------------------------------------------------------------------------------------------------------
RESET:
;Начальная_инициализация
;--------------------------------------------------------------------------------------------------------------
ldi R16, Low(RAMEND); Инициализация стека
out SPL, R16
ldi R16, High(RAMEND)
out SPH, R16
.def try = r21
.def temp = r16
.def razr1 = r17
.def razr2 = r18
.def razr3 = r19
.equ W1_DDR = DDRB ; Присваиваем псевдоним регистрам порта датчиков DS18B20
.equ W1_PORT = PORTB
.equ W1_PIN = PINB
.equ W1_BIT = 0 ; Бит порта на котором датчики (8 цифровой контакт на плате Ардуино)
.equ FREQ = 16000000 ; Частота процессора
.equ baudrate = 38400; Рассчитываем делитель бодрейта для UART
.equ bauddivider = FREQ/(16*baudrate)-1
.equ FreqSCL = 200000; Рассчитываем частоту работы шины TWI
.equ FreqTWBR = ((FREQ/FreqSCL)-16)/2
ldi R16, low(bauddivider); Инициализация UART
sts UBRR0L,R16
ldi R16, high(bauddivider)
sts UBRR0H,R16
ldi R16,0
sts UCSR0A, R16
sts UCSR0B, R16
sts UCSR0C, R16
LDI R16, (1<
\\
====Текст модуля 1wire.asm====
;1-wire_подпрограммы
;--------------------------------------------------------------------------------------------------------------
W1_Sbros: ; Сброс шины и проверка датчик на месте ли
lds r16, W1_BIT ; Записываем в r16 ножку где датчик
sbi W1_DDR, W1_BIT ; Ногу на выход
cbi W1_PORT, W1_BIT ; Опрокидываем вывод на землю
rcall W1_DelayH ; Задержка 480 мкс, для сброса
cbi W1_DDR, W1_BIT ; Ногу на вход
rcall W1_DelayI ; Ждем тайм слот 70 мкс
sbis W1_PIN, W1_BIT ; Пропускаем следующую строку, если бит порта в 1
ldi r17, 1 ; И установим сигнальный регистр в 1
sbic W1_PIN, W1_BIT ; Пропускаем следующую строку, если бит порта в 0
ldi r17, 0 ; И установим сигнальный регистр в 0
rcall W1_DelayJ ; Ждем тайм слот 410 мкс
ret ; Если датчик на месте, в r17 по выходу отсюда будет 1, в противном случае 0
W1_Address:
clr r20
read:
inc r20 ; Процедура считывания адреса датчиков DS18B20
in r10,SREG
push r10
lpm r16,Z
rcall ds_byte_wr
pop r10
out SREG,r10
adiw ZH:ZL,1
cpi r20,8
brne read
ret
W1_ReadMem: ; Чтение памяти регистров температуры
ldi r16, 0x55 ; Пошлем команду 0x55, это сравнить уникальный номер датчика
rcall ds_byte_wr ; Так как он у нас один на проводе
rcall W1_Address ; Отправляем адрес датчика
ldi r16, 0xBE ; Говорим датчику, что мы сейчас будем читать
rcall ds_byte_wr ; Запуливаем байт
rcall ds_byte_rd ; А тут уже начинаем читать, прочитали первый
st Y, r16 ; И запулили его в память, по метке Trm
adiw YH:YL,1
rcall ds_byte_rd ; Читаем второй
st Y, r16 ; И запулили его в память, по метке Trm+1
adiw YH:YL,1
rcall W1_Sbros ; Сбрасываем шину и проверяем есть ли датчик
;ldi ZL,LOW(Addr2*2)
;ldi ZH,HIGH(Addr2*2)
;clr r20
ret
W1_ConvTemp: ; Подпрограмма конвертирования температуры
ldi r16, 0xCC ; Пропускаем уникальный номер датчика
rcall ds_byte_wr
ldi r16, 0x44 ; Говорим что надо бы сконвертировать температуру, этот процесс занимает 750
rcall ds_byte_wr ; миллисекунд, поэтому идем что-то делать, или ленится
ret
W1_Init_12bit: ; Подпрограмма перестройки на 12 бит температуры
ldi r16, 0xCC ; Пропускаем уникальный номер датчика
rcall ds_byte_wr ; Спуливаем в датчик
ldi r16, 0x4E ; Говорим что сейчас будем писать в RAM регистры датчика
rcall ds_byte_wr ; Спуливаем в датчик
ldi r16, 0xFF ; 0xFF записываем в первые 2 регистра, это регистры температуры, он нам не
rcall ds_byte_wr ; нужен, поэтому их оставляем в стандартном состоянии
ldi r16, 0xFF ; 0xFF второй байт температуры
rcall ds_byte_wr ; Спуливаем на порт
ldi r16, 0x7F ; А вот тут говорим что 12 бит - 7F, или 1F - 9бит, 3F - 10 бит, 5F - 11 бит
rcall ds_byte_wr ; Спуливаем на порт
ret
ds_byte_rd: ; Подпрограмма чтения данных в регистр r16 с 1 Wire
ldi r17, 8 ; Пишем в r17 - 8, т.к. у нас в бит в регистре
clr r16 ;Чистим r16, сюда будем читать данные
ds_byte_rd_0:
sbi W1_DDR, W1_BIT ; Вывод на выход
cbi W1_PORT, W1_BIT ; Опрокидываем вывод на землю
rcall W1_DelayA ; Ждем 6 микросекунд
cbi W1_DDR, W1_BIT ; Вывод на вход
rcall W1_DelayE ; Ждем 9 микросекунд
sbis W1_PIN, W1_BIT
clc ; Очищаем бит C = 0
sbic W1_PIN, W1_BIT
sec ; Очищаем бит C = 1
ror r16 ; Производим циклический сдвиг вправо через С
rcall W1_DelayF ; Ждем 55 микросекунд
dec r17 ;Понижаем на 1 регистр r17
brne ds_byte_rd_0 ; если не равен 0 вращаемся в цикле
ret
ds_byte_wr: ; Подпрограмма записи данных из регистра r16 в датчик
ldi r17, 8 ; Пишем в r17 - 8, т.к. у нас в бит в регистре
ds_byte_wr0:
sbi W1_DDR, W1_BIT ; Вывод на выход
cbi W1_PORT, W1_BIT ; Опрокидываем вывод на землю
sbrc r16, 0 ; Проверим, в r16 бит 0 очищен или установлен
rjmp ds_byte_write_1 ; Если установлен перейдем по этой метке
rjmp ds_byte_write_0 ; Если очищен перейдем по этой метке
ds_byte_wr1:
lsr r16 ; Логический сдвиг вправо
dec r17 ; Понижаем r17 на 1
brne ds_byte_wr0 ; Если не равен 0, вращаемся в цикле
ret ; Выход из подпрограммы
ds_byte_write_0: ; Запись 0
rcall W1_DelayC ; Ждем 60 микросекунд
cbi W1_DDR, W1_BIT ; Вывод на вход
rcall W1_DelayD ; Ждем 10 микросекунд
rjmp ds_byte_wr1
ds_byte_write_1: ; Запись 1
rcall W1_DelayA ; Ждем 6 микросекунд
cbi W1_DDR, W1_BIT ; Вывод на вход
rcall W1_DelayB ; Ждем 64 микросекунд
rjmp ds_byte_wr1
W1_DelayA: ; Задержка 6 mcs
ldi XH, high(FREQ/2000000)
ldi XL, low(FREQ/2000000)
rcall W1_Delay
ret
W1_DelayB: ; Задержка 64 mcs
ldi XH, high(FREQ/130000)
ldi XL, low(FREQ/130000)
rcall W1_Delay
ret
W1_DelayC: ; Задержка 60 mcs
ldi XH, high(FREQ/136000)
ldi XL, low(FREQ/136000)
rcall W1_Delay
ret
W1_DelayD: ; Задержка 10 mcs
ldi XH, high(FREQ/1000000)
ldi XL, low(FREQ/1000000)
rcall W1_Delay
ret
W1_DelayE: ; Задержка 9 mcs
ldi XH, high(FREQ/1200000)
ldi XL, low(FREQ/1200000)
rcall W1_Delay
ret
W1_DelayF: ; Задержка 55 mcs
ldi XH, high(FREQ/150000)
ldi XL, low(FREQ/150000)
rcall W1_Delay
ret
W1_DelayH: ; Задержка 480 mcs
ldi XH, high(FREQ/16664)
ldi XL, low(FREQ/16664)
rcall W1_Delay
ret
W1_DelayI: ; Задержка 70 mcs
ldi XH, high(FREQ/116000)
ldi XL, low(FREQ/116000)
rcall W1_Delay
ret
W1_DelayJ: ; Задержка 410 mcs
ldi XH, high(FREQ/19512)
ldi XL, low(FREQ/19512)
rcall W1_Delay
ret
W1_Delay: ; Подпрограмма воспроизведения задержки
sbiw XH:XL, 1 ; Вычитаем единицу из регистровой пары
brne W1_Delay ; Если не равно 0 крутимся в цикле
ret ; Выход из подпрограммы
;--------------------------------------------------------------------------------------------------------------
;Конец_1-wire_подпрограмм
\\
====Текст модуля hextobcd.asm====
.def fASCIIL =r16
.def tASCII0 =r16
.def fASCIIH =r17
.def tASCII2 =r25
.def tASCII3 =r19
.def tASCII4 =r20
.def tASCII1 =r21
.def cnt16a =r21
.def tmp16a =r22
.def tmp16b =r23
;***** Код
print_ascii:
check1 r20
check1 r19
check1 r25
check1 r21
check1 r16
ret
bin2ASCII15:
ldi tmp16a, low(10000)
ldi tmp16b, high(10000)
rcall bin2ASCII_digit
mov tASCII4, cnt16a
ldi tmp16a, low(1000)
ldi tmp16b, high(1000)
rcall bin2ASCII_digit
mov tASCII3, cnt16a
ldi tmp16a, low(100)
ldi tmp16b, high(100)
rcall bin2ASCII_digit
mov tASCII2, cnt16a
ldi tmp16a, low(10)
ldi tmp16b, high(10)
rcall bin2ASCII_digit
ldi r17,0x30
add r16,r17
add r21,r17
add r25,r17
add r19,r17
add r20,r17
rcall print_ascii
ret
bin2ASCII_digit:
ldi cnt16a, -1
bin2ASCII_digit_loop:
inc cnt16a
sub fASCIIL, tmp16a
sbc fASCIIH, tmp16b
brsh bin2ASCII_digit_loop
add fASCIIL, tmp16a
adc fASCIIH, tmp16b
ret
\\
====Текст модуля iterrupts.asm====
;Векторы_прерываний
;-----------------------------------------------------------------------
.org 0x0000 ; Reset
jmp RESET
.org 0x0002 ; External Interrupt Request 0
reti
.org 0x0004 ; External Interrupt Request 1
reti
.org 0x0006 ; Pin Change Interrupt Request 0
reti
.org 0x0008 ; Pin Change Interrupt Request 1
reti
.org 0x000A ; Pin Change Interrupt Request 2
reti
.org 0x000C ; Watchdog Time-out Interrupt
reti
.org 0x000E ; Timer/Counter 2 Compare Match A
reti
.org 0x0010 ; Timer/Counter 2 Compare Match B
reti
.org 0x0012 ; Timer/Counter 2 Overflow
jmp Board_timer
.org 0x0014 ; Timer/Counter 1 Capture Event
reti
.org 0x0016 ; Timer/Counter 1 Compare Match A
reti
.org 0x0018 ; Timer/Counter 1 Compare Match B
reti
.org 0x001A ; Timer/Counter 1 Overflow
rjmp W1_timer
;reti
.org 0x001C ; Timer/Counter 0 Compare Match A
reti
.org 0x001E ; Timer/Counter 0 Compare Match B
reti
.org 0x0020 ; Timer/Counter 0 Overflow
reti
.org 0x0022 ; SPI Serial Transfer Complete
reti
.org 0x0024 ; USART, Rx Complete
reti
.org 0x0026 ; USART, UDR Empty
reti
.org 0x0028 ; USART, Tx Complete
reti
.org 0x002A ; ADC Conversion Complete
jmp adc_conv
.org 0x002C ; EEPROM Ready
reti
.org 0x002E ; Analog Comparator
reti
.org 0x0030 ; Two-wire Serial Interface
jmp TWI_int
;reti
.org 0x0032 ; Store Program Memory Read
reti
.org INT_VECTORS_SIZE
\\
====Текст модуля macr.asm====
;Макросы
;-----------------------------------------------------------------------
.macro pushf
push r10
in r10,SREG
push r10
.endm
.macro popf
pop r10
out SREG,r10
pop r10
.endm
.macro pushr
push r10
in r10,SREG
push r10
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
push r25
push r26
push r27
push r28
push r29
.endm
.macro popr
pop r29
pop r28
pop r27
pop r26
pop r25
pop r23
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
pop r10
out SREG,r10
pop r10
.endm
.macro check
push @0
lds @0,@1
sts UDR0,@0
check_loop: ldi @0,UCSR0A
sbrs @0,TXC0
rjmp check_loop
pop @0
rcall Delay
.endm
.macro check_kosv
push @0
ld @0,@1
sts UDR0,@0
check_loop1: ldi @0,UCSR0A
sbrs @0,TXC0
rjmp check_loop1
pop @0
rcall Delay
.endm
.macro check1
sts UDR0,@0
check_loop1: ldi @0,UCSR0A
sbrs @0,TXC0
rjmp check_loop1
rcall Delay
.endm
.macro tabulate
push r16
ldi r16,0x09
check1 r16
pop r16
.endm
.macro newline
push r16
ldi r16,0x0D
check1 r16
ldi r16,0x0A
check1 r16
pop r16
.endm
\\
====Текст модуля twi_lib.asm====
;Библиотека_TWI
;-----------------------------------------------------------------------
;======= Стартовая посылка по шине i2c =================================================
i2c_start:
push r16
ldi r16,(1<