пятница, 15 апреля 2016 г.

Обучение управлению RC самолетом

Продолжение предыдущей статьи. Здесь представлена новая программа и рассказано об основных отличиях этой программы от кода показанного в ранее опубликованной статье.


Сначала напомню основные моменты. Предлагаемое устройство должно способствовать облегчению процесса обучения управлению радиоуправляемым самолетом. Само устройство состоит из Arduino Pro Mini ATmega328P (5V, 16MHz). К ардуино подключаются два приемника (учителя и ученика) и электроника самолета (ESC и сервоприводы). Подробное описание соединения компонентов представлено ниже.

Подключение приемника от аппаратуры учителя к ардуино

Arduino --- Receiver
D0 --- CH1 (AIL)
D1 --- CH2 (ELE)
D2 --- CH5 (тумблер)
D3 --- CH3 (THR)
D4 --- CH4 (RUD)
VCC --- VCC
GND --- GND

Подключение приемника от аппаратуры ученика к ардуино

Arduino --- Receiver
A0 --- CH1 (AIL)
A1 --- CH2 (ELE)
A3 --- CH3 (THR)
A4 --- CH4 (RUD)
VCC --- VCC
GND --- GND

Подключение электроники самолета к ардуино

Arduino --- Airplane
D8 --- AIL (сервоприводы элеронов)
D9 --- ELE (сервопривод руля высоты)
D11 --- THR (регулятор электродвигателя)
D12 --- RUD (сервопривод руля направления)

Земля всех компонентов соединяется с землей ардуино. Питание всех компонентов также соединяется с питанием ардуино.

Выше приведен один из возможных вариантов подключения электронных компонентов самолета к ардуино. Всё зависит от конструкции конкретного самолета.

Программа

;---------------------------------------------
; Program      : Trainer
; Compiler     : Atmel Studio
; Chip type    : ATmega328P
; System Clock : 16 MHz
; Date         :
;---------------------------------------------

.include "m328Pdef.inc"

;---------------------------------------------
; Interrupt vectors

        .cseg

        .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
        reti
        .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
        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
        reti
        .org 0x002C    ; EEPROM Ready
        reti
        .org 0x002E    ; Analog Comparator
        reti
        .org 0x0030    ; Two-wire Serial Interface
        reti
        .org 0x0032    ; Store Program Memory Read
        reti

        .org INT_VECTORS_SIZE

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

RESET:

;---------------------------------------------
; Steck initialization

        ldi R16, Low(RAMEND)
        out SPL, R16
        ldi R16, High(RAMEND)
        out SPH, R16

;---------------------------------------------
; USART

        ldi R16, 0
        sts UBRR0H, R16
        sts UBRR0L, R16
        sts UCSR0A, R16
        sts UCSR0B, R16
        sts UCSR0C, R16

;---------------------------------------------
; Input/Output ports

        ldi R16, 0b00000000
        out PORTB, R16

        ldi R16, 0b00111111
        out DDRB, R16

        ldi R16, 0b00000000
        out PORTC, R16

        ldi R16, 0b00000000
        out DDRC, R16

        ldi R16, 0b00000000
        out PORTD, R16

        ldi R16, 0b00000000
        out DDRD, R16

;---------------------------------------------
; Timer/Counter 2

        ldi R16, 0
        sts TCNT2, R16

        ; COM2A1 COM2A0 COM2B1 COM2B0 - - WGM21 WGM20
        ldi R16, 0
        sts TCCR2A, R16

        ; FOC2A FOC2B - - WGM22 CS22 CS21 CS20
        ldi R16, ((1<<CS22)|(1<<CS21))
        sts TCCR2B, R16

        ; - - - - - OCIE2B OCIE2A TOIE2
        ldi R16, 0
        sts TIMSK2, R16

        ; - - - - - OCF2B OCF2A TOV2
        ldi R16, 0
        sts TIFR2, R16

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

        eor R16, R16
        eor R17, R17
        eor R18, R18
        eor R19, R19
        eor R20, R20
        eor R21, R21
        eor R22, R22

;---------------------------------------------
; Global interrupt disable

        cli

;---------------------------------------------
; main

main:

        cpi R17, 94
        brsh l_01 ; R17 >= 94

        cpi R18, 1
        brne l_02 ; R18 != 1

        in R19, PIND
        out PORTB, R19

        sbic PIND, 2 ; (PIND & 0b00000100) == 0
        rjmp l_03

        cpi R20, 1
        brne main ; R20 != 1

        lds R17, TCNT2

        ldi R20, 0

        rjmp main

l_03:
        cpi R20, 0
        brne main ; R20 != 0

        sts TCNT2, R16

        ldi R20, 1

        rjmp main

l_02:
        cpi R18, 2
        brne l_04 ; R18 != 2

        in R19, PINC
        out PORTB, R19

        andi R19, 0b00011011
        brne main ; R19 != 0

        ldi R18, 0

        rcall delay ; delay

        rjmp main

l_04:

        in R19, PIND
        andi R19, 0b00011011
        brne main ; R19 != 0

        ldi R18, 1

        rjmp main

l_01:

        cpi R18, 2
        brne l_05 ; R18 != 2

        in R19, PINC
        out PORTB, R19

        sbic PIND, 2 ; (PIND & 0b00000100) == 0
        rjmp l_06

        cpi R20, 1
        brne main ; R20 != 1

        lds R17, TCNT2

        ldi R20, 0

        rjmp main

l_06:
        cpi R20, 0
        brne main ; R20 != 0

        sts TCNT2, R16

        ldi R20, 1

        rjmp main

l_05:

        cpi R18, 1
        brne l_07 ; R18 != 1

        in R19, PIND
        out PORTB, R19

        andi R19, 0b00011011
        brne main ; R19 != 0

        ldi R18, 0

        rcall delay ; delay

        rjmp main

l_07:

        in R19, PINC
        andi R19, 0b00011011
        brne main ; R19 != 0

        ldi R18, 2

        rjmp main

;---------------------------------------------
; wait 10 ms

delay:

        ldi R21, 0x40 ; Low byte
        ldi R22, 0x9C ; High byte

l_10:

        subi R21, 1
        sbci R22, 0
        brcc l_10

        ret

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

Программа написана на ассемблере. Как превратить такой код в прошивку я уже рассказывал http://justforduino.blogspot.ru/2014/10/atmel-studio-6.html Про загрузку получившейся прошивки в ардуино тоже уже писал http://justforduino.blogspot.ru/2014/10/arduino.html

Достоинством ассемблерной программы является относительная простота расчета времени выполнения кода. Чтобы оценить время выполнения скетча ардуино необходимо дизассемблировать прошивку созданную Arduino IDE. Это несколько сложнее и менее надежно. Возможны ошибки.

Итак. Программа обновляет порт B (D8, D9, D10, D11, D12, D13) максимум за 17 тактов. Контроллер работает на частоте 16 MHz. Значит за одну микросекунду выполняется 16 тактов. Таким образом программа может изменять длительность управляющих импульсов на плюс минус одну микросекунду (сервоприводы такие колебания не замечают).

((1.0 / 16000000.0) * 17.0 ) * 1000000.0 = 1.0625 us

Управляющие импульсы не выходят за допустимые границы и всегда находятся в пределах от 1000 до 2000 микросекунд.

Между управляющими импульсами должна присутствовать пауза. Её длительность обычно составляет от 18000 до 19000 микросекунд. Не значительные изменения времени паузы никак не проявляются. В представленной программе, в момент переключения управления, возможно изменение длительности паузы от 10000 до 30000 микросекунд. Это ни какого влияния ни на что не оказывает.

Показанная в этой статье программа сильно лучше скетча из прошлой статьи. Она работает довольно быстро и достаточно точно.