Это старая версия документа!
АННОТАЦИЯ
В данном программном документе приведен текст программы «telemetry_rev1-1», предназначенной для опроса телеметрических датчиков, подключенных к микроконтроллеру Atmel Atmega328P, и отправки полученных данных по последовательному порту на бортовую ЭВМ. Текст программы реализован в виде символической записи на языке ассемблера AVR. Компилятором является консольная программа для UNIX-подобных операционных систем - avra.
Основной функцией программы telemetry_rev1-1 является опрос и отправка показаний следующих датчиков:
В процессе сбора данных происходит их перевод в двоично-десятичный код и отправка по USB-to-Serial протоколу с сохранением в памяти бортовой ЭВМ.
Программа состоит из основной программы,подключаемых модулей и файла объявления имен регистров:
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>