АННОТАЦИЯ

В данном программном документе приведен текст программы «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 - объявление имен регистров и ячеек памяти.

1. Текст программы telemetry_rev1-1.asm на языке ассемблера AVR

<code asm>;-------------------------------------------------------------------------------------------------------------- ; Program : telemetry_rev1-1 ; Compiler : AVRA ; Chip type : ATmega328P ; System Clock : 16 MHz ; Date : 18.01.2017 ;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Подключение_библиотек_и_резервация---------------------------------------- ;--------------------------------------------места_под_данные-------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

.include "m328Pdef.inc"

;--------------------------------------------------------------------------------------------------------------

.dseg

adc_data:

.byte	8

Trm: ; Ячейки ОЗУ под показания датчиков DS18B20

.byte	14

bmp_temp: ; Ячейки ОЗУ под показания темп. датчика BMP180

.byte	2

bmp_pres: ; Ячейки ОЗУ под показания давления датчика BMP180

.byte	2

bh_lux:

.byte	2

;--------------------------------------------------------------------------------------------------------------

.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<<RXEN0)|(1<<TXEN0)|(0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)
sts	UCSR0B, R16

ldi r16,0

LDI	R16, (0<<USBS0)|(0<<UMSEL0)|(0<<UMSEL1)|(1<<UCSZ00)|(1<<UCSZ01)
sts	UCSR0C, R16
ldi	r16,0b01000000	; Инициализация АЦП
sts	ADMUX,r16
ldi	r16,0b11011111
sts	ADCSRA,r16
ldi	r25,0

rcall	W1_Sbros	; Сбрасываем шину 1-Wire
rcall	W1_Init_12bit	; Перестраиваем конфигурационный байт на 12 битную схему работы
rcall	W1_Sbros	; Вновь сбрасываем

ldi	r16,0b00000101	; Инициализация работы прерывания по таймеру для отправки показаний
sts	TCCR2B,r16	; датчиков DS18B20 по шине 1-Wire
ldi	r16,0b00000001
sts	TIMSK2,r16
sts	TIFR2,r16
ldi	r16,0xF0
sts	TCNT2,r16

ldi	r16,0b00000101	; Инициализация работы прерывания по таймеру для отправки показаний
sts	TCCR1B,r16	; датчиков DS18B20 по шине 1-Wire
ldi	r16,0b00000001
sts	TIMSK1,r16
sts	TIFR1,r16
ldi	r16,0xA0
sts	TCNT1H,temp
sts	TCNT1L,temp
ldi	R16, 0b00110000	; Инициализация шины TWI
out	PORTC, R16
ldi	r16,FreqTWBR
sts	TWBR,r16
ldi	r16,0x00
sts	TWSR,r16

clr	try
clr	r2
clr	r3
clr	r4
clr	r5
ldi	r24,0x00
ldi R16, 0b00000000
out PORTD, R16

ldi R16, 0b00000100
out DDRD, R16
rcall	i2c_start
ldi	r16,0x46	; Если нет, то остаемся этой процедуре
rcall	i2c_send
ldi	r16,0x11
rcall	i2c_send
rcall	i2c_stop
sei	; Разрешаем прерывания
rjmp	main

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Конец_начальной_инициализации------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Основная_подпрограмма------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

main:

cli	; Запрещаем прерывания в основном цикле
;rcall	Board_timer

;----------------------print_time-------------------

pushr
mov	r17,r5
mov	r16,r4
rcall	bin2ASCII15
mov	r17,r3
mov	r16,r2
rcall	bin2ASCII15
popr
tabulate

;---------------------------------------------------

rcall	W1_ConvTemp	; Говорим датчикам конвертировать температуры
push	r16
lds	r16,bh_lux+1
cpi	r16,0x82
brlo	light
brsh	no_light

light:

lds	r16,bh_lux
cpi	r16,0x01
brsh	light_exit
ldi	r16,0b00000100
out	PORTD,r16
jmp	light_exit

no_light:

ldi	r16,0b00000000
out	PORTD,r16
jmp	light_exit

light_exit:

pop	r16
ldi	ZL, LOW(adc_data)	; Производим косвенную адресацию на показания АЦП, чтобы потом их
ldi	ZH, HIGH(adc_data)	; последовательно отправить по UART
clr	r20

print_adc: ; Цикл отправки показаний АЦП

inc	r20
pushr
ld	r17,Z
adiw	ZH:ZL,1
ld	r16,Z
adiw	ZH:ZL,1
rcall	bin2ASCII15
popr
tabulate
cpi	r20,4
brne	print_adc	; Закончили цикл
tabulate

pushr
lds	r17,bmp_temp	; Выводим показания датчика BMP180
lds	r16,bmp_temp+1
rcall	bin2ASCII15
tabulate
lds	r17,bmp_pres
lds	r16,bmp_pres+1
rcall	bin2ASCII15
tabulate
lds	r17,bh_lux
lds	r16,bh_lux+1
rcall	bin2ASCII15
popr

inc	r24	; Увеличили счетчик кадров и отправили его значение по UART
newline

rcall	i2c_start
ldi	r16,0x47
rcall	i2c_send
rcall	i2c_receive
sts	bh_lux,r16
rcall	i2c_receive_last
sts	bh_lux+1,r16
rcall	i2c_stop
sei	; Разрешили прерывания, чтобы микроконтроллер смог обработать
; новые показания АПЦ, термометров и датчика BMP180
rcall	i2c_start
rjmp	main

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Конец_основной_подпрограммы------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Прерывание_АЦП------------------------------------------------------------ ;-------------------------------------------------------------------------------------------------------------- adc_conv:

cli	; Запрещаем прерывания
;rcall	Board_timer
lds	r17,ADCL	; Снимаем показания сделанные во время нашего отсутствия в прерывании
lds	r18,ADCH

cpi	r25,0	; Смотрим, какое число содержится в r25
breq	adc0	; Переходим по метке для этого числа
cpi	r25,1
breq	adc1
cpi	r25,2
breq	adc2
cpi	r25,3
breq	adc3
cpi	r25,4
breq	adc4

adc0: sts adc_data+6,r18 ; Записываем снятые в начале показания АЦП

sts	adc_data+7,r17
ldi	r16,0b01000000	; Запускаем новое преобразования в зависимости от пина АЦП
sts	ADMUX,r16
rjmp	adc_ex	; Переходим на выход

adc1: sts adc_data,r18

sts	adc_data+1,r17
ldi	r16,0b01000001
sts	ADMUX,r16
rjmp	adc_ex

adc2: sts adc_data+2,r18

sts	adc_data+3,r17
ldi	r16,0b01000010
sts	ADMUX,r16
rjmp	adc_ex

adc3: sts adc_data+4,r18

sts	adc_data+5,r17
ldi	r16,0b01000011
sts	ADMUX,r16
rjmp	adc_ex

adc4: clr r25 ; Если r25 = 4, очищаем его и прыгаем на adc0

rjmp	adc0

adc_ex: ldi r16,0b11011111 ; Каждый раз повторно инициализируем АЦП

sts	ADCSRA,r16
inc	r25
sei
reti

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Конец_прерывания_АЦП------------------------------------------------------ ;--------------------------------------------------------------------------------------------------------------

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Прерывание_таймера1-------------------------------------------------------- ;-------------------------------------------------------------------------------------------------------------- W1_timer:

cli
;rcall	Board_timer
ldi	r16,0x54
check1	r16
tabulate
rcall	W1_Sbros	; Сбрасываем шину и проверяем есть ли датчик
ldi	YL, LOW(Trm)
ldi	YH, HIGH(Trm)
ldi	ZL,LOW(Addr1*2)
ldi	ZH,HIGH(Addr1*2)
rcall	W1_ReadMem	; Читаем в ОЗУ текущую температуру
ldi	ZL,LOW(Addr2*2)
ldi	ZH,HIGH(Addr2*2)
rcall	W1_ReadMem
ldi	ZL,LOW(Addr3*2)
ldi	ZH,HIGH(Addr3*2)
rcall	W1_ReadMem
ldi	ZL,LOW(Addr4*2)
ldi	ZH,HIGH(Addr4*2)
rcall	W1_ReadMem
ldi	ZL,LOW(Addr5*2)
ldi	ZH,HIGH(Addr5*2)
rcall	W1_ReadMem
ldi	ZL,LOW(Addr6*2)
ldi	ZH,HIGH(Addr6*2)
rcall	W1_ReadMem
ldi	ZL,LOW(Addr7*2)
ldi	ZH,HIGH(Addr7*2)
rcall	W1_ReadMem
pushf	; На всякий случай отправляем в стек SREG
ldi	ZL, LOW(Trm)	; Делаем косвенную адресацию на массив данных температуры датчиков DS18B20
ldi	ZH, HIGH(Trm)
clr	r20

print1: ; Цикл печати данных

inc	r20
pushr
ld	r16,Z
adiw	ZH:ZL,1
ld	r17,Z
adiw	ZH:ZL,1
rcall	bin2ASCII15
tabulate
popr
cpi	r20,7

brne	print1
newline
; Задаем число с которого таймер начнет считать до следующего прерывания
ldi	r22,0xA0
sts	TCNT1H,r22
sts	TCNT1L,r22
popf	; Возвращаем SREG из стека
sei	; Разрешаем прерывания
reti

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Конец_прерывания_таймера1-------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Прерывание_таймера0-------------------------------------------------- ;-------------------------------------------------------------------------------------------------------------- Board_timer:

cli
;lds	r16,TIFR2
;andi	r16,0b00000001
;sbrs	temp,TOV2
;ret
pushf
mov	r16,r2
cpi	r16,0xFF
breq	r2cap
inc	r2
jmp	btim_exit

r2cap:

clr	r2
mov	r16,r3
cpi	r16,0xFF
breq	r3cap
inc	r3
jmp	btim_exit

r3cap:

clr	r3
mov	r16,r4
cpi	r16,0xFF
breq	r4cap
inc	r4
jmp	btim_exit

r4cap:

clr	r4
mov	r16,r5
cpi	r16,0xFF
breq	r5cap
jmp	btim_exit
inc	r5

r5cap:

clr	r5
jmp	btim_exit

btim_exit:

ldi	r22,0xE9
sts	TCNT2,r22
popf
sei
reti

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------Конец_прерывания_таймера0-------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

;-------------------------------------------------------------------------------------------------------------- ;------------------------------------------Прерывание_TWI------------------------------------------------------ ;-------------------------------------------------------------------------------------------------------------- TWI_int:

cli	; Запрещаем прерывания
;rcall	Board_timer

push	r16	; Отправляем в стек r16
lds	r16,TWSR	; Сравниваем Статус-регистр TWI
andi	r16,0xF8
cpi	r16,0x08	; Если он равен 0x08, то нам надо запустить вычисление
breq	TWI_start_samples_1
cpi	r16,0x10	; Если он равен 0х10, то нам надо считывать показания
breq	TWI_read_samples_1

TWI_exit: pop r16 ; Возвращаем r16 из стека

sei	; Разрешаем прерывания
reti

TWI_start_samples_1: ; Запускаем вычисление некомпенсированной температуры

; Проверяем содержимое переменной try
cpi	try,1	; Если 1, то прыгаем на запуск вычисления давления
breq	TWI_start_samples_2
ldi	r16,0xEE	; Если нет, то остаемся этой процедуре
rcall	i2c_send

ldi	r16,0xF4
rcall	i2c_send

ldi	r16,0x2E
rcall	i2c_send
rcall	i2c_start
rjmp	TWI_exit

TWI_start_samples_2: ; Запускаем вычисление некомпенсированного давления

ldi	r16,0xEE
rcall	i2c_send

ldi	r16,0xF4
rcall	i2c_send
ldi	r16,0x34
rcall	i2c_send
rcall	i2c_start
rjmp	TWI_exit

TWI_read_samples_1: ; Считываем показания некомпенсированной температуры

; Снова проверяем try
cpi	try,1
breq	TWI_read_samples_2
ldi	r16,0xEE
rcall	i2c_send

ldi	r16,0xF6
rcall	i2c_send
rcall	i2c_start

ldi	r16,0xEF
rcall	i2c_send

rcall	i2c_receive
sts	bmp_temp,r16
rcall	i2c_receive_last
sts	bmp_temp+1,r16
ldi	try,0b00000001
rcall	i2c_stop
rjmp	TWI_exit

TWI_read_samples_2:

ldi	r16,0xEE
rcall	i2c_send

ldi	r16,0xF6
rcall	i2c_send
rcall	i2c_start

ldi	r16,0xEF
rcall	i2c_send

rcall	i2c_receive
sts	bmp_pres,r16
rcall	i2c_receive_last
sts	bmp_pres+1,r16
ldi	try,0b00000000	; Уменьшаем try до 0
rcall	i2c_stop
rjmp	TWI_exit

;-------------------------------------------------------------------------------------------------------------- ;--------------------------------------Конец_прерывания_TWI---------------------------------------------------- ;--------------------------------------------------------------------------------------------------------------

Addr1: .db 0x28,0xFF,0x41,0x3,0xA3,0x15,0x1,0xBB ; Адреса датчиков DS18B20 Addr2: .db 0x28,0xFF,0xE2,0x1A,0xA2,0x15,0x4,0xF9 Addr3: .db 0x28,0xFF,0x91,0xF,0xA3,0x15,0x4,0xC8 Addr4: .db 0x28,0xFF,0x90,0x82,0xA3,0x15,0x4,0x41 Addr5: .db 0x28,0xFF,0xC6,0xB,0xA3,0x15,0x4,0x2 Addr6: .db 0x28,0xFF,0x9,0x5D,0xA3,0x15,0x4,0x9E Addr7: .db 0x28,0xFF,0x7F,0x83,0xA3,0x15,0x4,0x7B

Delay: ; Стандартная задержка

pushf
ldi	razr1, 50
ldi	razr2, 10

Pdelay:

dec	razr1
brne	Pdelay
dec	razr2
brne	Pdelay
popf
ret

Delay1: ; Стандартная задержка 1

pushf
ldi	razr1, 50
ldi	razr2, 50
ldi	razr3, 1

Pdelay1:

dec	razr1
brne	Pdelay1
dec	razr2
brne	Pdelay1
dec	razr3
brne	Pdelay1
popf
ret

</code asm>