21

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

у мене зіпсувався настрій, коли я дізнався, що лише 2 наявних у мене пульти працюють у цьому стандарті.

унiверсальний декодер

Подякували: 0xDADA11C7, leofun01, MCS-513

22 Востаннє редагувалося MCS-51 (18.02.2016 22:57:16)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Доброго дня, громадяни слухачі))
Інколи буває так, що кнопок потрібно більше 3-4, а виводів більше немає. І клавіатуру матричну не зробиш, а клавіатуру на базі АЦП мій контроллер зробити не дозволяє. За мотивами https://www.youtube.com/watch?v=_NySIlQAVXA#t=14 було вирішего зробити свій варіант. Купляти нічого не хотілося, і з якогось гімна було добуто 2 сдвигових регістри комуністичного періоду під назвами К1533ИР9 та К555ИР10 (закордонні аналоги відповідно 74НС165 та 74НС166). Погугливши виявив, що вони обидва підходять під мої цілі, але ИР10 дещо "навороченіший", тому було вирішено залишити його для майбутніх експериментів.
Схема підключення 74HC165 прикріплена, для 74HC166 схема зовсім інша, гугліть.
Код аж смішно було під спойлер ховати, такий об'ємний. Я схему паяв 2 дні, а писав його 3 хвилини))) Але може комусь згодиться. 74HC165 особлива тим, що має 2 виходи, звичайний та інверсний (виводи 9 та 7), відповідно можна при усіх потушених світлодіодах 1 запалити, або при усіх запалених 1 потушити. А можна і не 1, схема підтримує одночасні натискання будь-яких комбінацій клавіш. Алгоритм погашення брязкоту кнопок не закладено, кому потрібно - реалізуйте самі. У мене й без нього нормально працює (поки кнопки новенькі).

Прихований текст

74HC165.asm

LED_Port    equ    P0
counter     equ     R7
clock       equ     P1.7        ; вивід клоку (2 вивід 74HC165)
load        equ     P1.6        ; вивід защіпки (1 вивід 74HC165)
data_       equ     P1.5        ; вивід прийомника бітів  (7 вивід 74HC165)

Main:
    clr     load                    ; опустимо защіпку - закинемо стан виводів у внутрішній регістр 74HC165 (К555ИР9)
    mov     counter,#8              ; задамо лічильник
    setb    load                    ; піднімемо защіпку
Repeat:
    clr     clock                   ; дригнемо clock-ом
    mov     C,    data_           ; перенесемо стан біта у накопичувач
    rlc     A                       ; задвинемо у Акумулятор
    setb    clock                   ; піднімемо clock
    djnz    counter,Repeat          ; повторюємо для всіх 8 біт
    mov     LED_Port,A        ; виведемо на світлодіоди
    jmp     Main                    ; зациклимося
Post's attachments

74HC165 Schematic.jpg 97.3 kb, 239 downloads since 2016-02-18 

Подякували: 0xDADA11C71

23 Востаннє редагувалося raxp (18.02.2016 18:57:05)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

кнопок потрібно більше 3-4, а виводів більше немає. І клавіатуру матричну не зробиш, а клавіатуру на базі АЦП мій контроллер зробити не дозволяє

рішень схемотехнiчних безліч. Завжди можна підключити кнопки у вигляді RC подільника до генератора, керованого напругою - ГКН (на тій же 555). Вимірювати відповідно період/частоту на цифровому вході.

А ще АЦП можна зімітувати на цифровому вході, використовуючи заряд-розряд ємності до рівня лог.1 і 0 на RC-ланцюжках.

Подякували: MCS-51, 0xDADA11C72

24

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Дякую за варіанти, можливо колись стануть у нагоді))

25 Востаннє редагувалося MCS-51 (18.03.2016 22:12:29)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Рівно місяць минув з тих пір, як я тут останній раз щось писав. Час виправляти ситуацію. Публікую свою власну розробку. Сенсорну клавіатуру з виводом інформації по UART. Впринципі нічого особливого, якщо не брати до уваги те, що під MCS-51 я не знаходив нічого подібного, усе під ПІК-и або АВР-и. Отже код:

Прихований текст
; Всі виводи Р0 підтягуємо до VCC резисторами номіналом 1 МОм.
; 1. Порт клавіатури скидається в нуль (mov KeyPort, #0).
; 2. Весь порт клавіатури встановлюється в 1 (mov KeyPort, #0FFh). Так як до цього на ніжках був нуль, а елементи електричного кола ніжки мають певну ємність, то починається процес заряду цих ємностей через зовнішні резистори підтяжки 1 МОм.
; 3. З моменту підняття ніжок починається відлік часу (setb TR0) з контролем стану ніжок (mov A, KeyPort), періодично перевіряємо коли зарядиться весь порт (cjne A,#0FFh,NoPush).
; 4. Як тільки весь порт зарядитися, зупиняємо лічильник. Отримане значення лічильника і є ємність останньої заярженой ніжки в "попугаях". За величиною цього значення і можна судити про те, чи є дотик до сенсора.
; При дотику час зарядки сенсора буде збільшуватися, таким чином значення рахункового регістра Timer0 буде більше каліброваного значення, записаного в змінних SensorH і SensorL
;==============================================================================================================================================================================================================================
KeyPort    equ    P0                ; порт клавіатури

Key_1    equ    P0.0                ; Визначення окремих сенсорів
Key_2    equ    P0.1
Key_3    equ    P0.2
Key_4    equ    P0.3
Key_5    equ    P0.4
Key_6    equ    P0.5
Key_7    equ    P0.6
Key_8    equ    P0.7
zumer    equ    P3.5

SensorH        equ    R5            ; Старший байт попугаїв чутливості сенсора
SensorL        equ    R4            ; Молодший байт попугаїв чутливості сенсора
CodeSensor    equ    R3            ; Змінна, скинутих біт якої показує останній заряджений (а значить можливо натиснутий) сенсор
symbol        equ    R2            ; Змінна, в якій зберігається код натиснутої клавіші
; Не застосовувати R0 і R1 Банку 0, використовуються для формування затримки

dseg    at    7h                ; Перших 8 регістрів (з R0 по R7 0 банку) резервуємо
                                            ; Тут обявляємо наші змінні, якщо вони потрібні
Stack:                        ; А далі піде стек

cseg    at      0
    jmp    Start
org    0Bh
    jmp    Scan_TouchPad               ; Опитування сенсорної клавіатури по Timer0

org 30h

Start:
    mov     SP,#Stack                  ; Визначаємо початок стеку
    clr    F0                      ; Очищаємо буфер натиснутого сенсора

Init_Timer0:                    ; ініціалізуємо Timer0
    mov    TMOD,    #1            ; Режим роботи - №2 (16-біт)
    mov    TH0,    #0h            ; Занесемо початкове значення в рахунковий регістр Timer0
    mov    TL0,    #0h

Calibration:                    ; Калібрування по Key_1
    clr    Key_1                ; Скидаємо 1 сенсор в 0
    nop                    ; Потупимо 1 мкс, щоб на ніжці встановилося значення
    setb    Key_1                ; Піднімаємо 1 сенсор
    setb    TR0                ; запускаємо Timer0

Calibration_again:
        mov    C,    Key_5            ; зчитуємо стан 1 сенсора
    jnc    Calibration_again        ; не зарядився? знову перевіряємо
    clr    TR0                ; Зарядився. Зупинимо Timer0
    clr    C                ; Очистимо біт перенесення, нам скоро перевіряти молодший байт попугаїв сенсора на переповнення
    mov    A,    TL0            ; Занесемо молодший байт значення лічильника Timer0
    add    A,    #1            ; Додамо для гістерезису
    jc    OverflowSensorL            ; Перевіримо на переповнення
    mov    SensorL,A            ; І занесемо в молодший байт папуг сенсора
    mov    A,    TH0            ; Занесемо старший байт значення лічильника Timer0
    jmp    MoveTHO_2_SensorH        ; Переповнення молодшого байта не було, тому просто заносимо значення в старший байт попугаїв сенсора

OverflowSensorL:                ; Було переповнення молодшого байта, потрібно додати для гістерезису в старшому
    mov    A,    TH0            ; Занесемо старший байт значення лічильника Timer0
    add    A,    #1            ; Додамо для гістерезису
    clr    C                ; Знову очистимо біт перенесення, нам скоро перевіряти значення папуг сенсора на більше / менше

MoveTHO_2_SensorH:
    mov    SensorH, A            ; І занесемо в старший байт попугаїв сенсора
    mov    TH0, #4Ch            ; Заново занесемо значення в рахунковий регістр Timer0
    mov    TL0, #0h            ; При цьому значенні переривання виникає кожні 50 мс
    setb    ET0                ; Дозволяємо переривання від Timer0
    setb    TR0                ; запускаємо Timer0

Init_UART:
    mov    SCON,    # 50h            ; Uart in mode 1 (8 bit), REN = 1
    orl    TMOD,    # 20h            ; Timer 1 in mode 2
    mov    TH1,    # 0FDh            ; 9600 Bds at 11.059MHz
    mov    TL1,    # 0FDh            ; 9600 Bds at 11.059MHz
    setb    ES                ; Дозволили переривання від послідовного порту
    setb    TR1                ; запуск Timer1
    setb    EA                ; Дозволити усі переривання

Main:                        ; Перевіряємо, чи було торкання до сенсора
        jbc    F0,Send_Symbol            ; Якщо було, переходимо на мітку, и тут же забуваємо, що торкалися сенсора
    jmp    Main            ; Не було торкання сенсора, крутимося у циклі

Send_Symbol:                    ; було торкання до сенсора
    mov    SBUF,    symbol            ; відправимо байт
    jnb    TI,    $            ; Немає флага ТІ (закінчення посилки) - чекаємо на місці
    clr    TI                ; Був флаг ТІ, байт пішов, скинемо флаг ТІ
    setb      zumer       ; закінчимо озвучку торкання сенсора

        jmp    Main            ; Ну і знову йдемо перевіряти сенсор


Scan_TouchPad:
    clr    EA                ; Заборонимо усі переривання
    clr    TR0                ; Зупинимо Timer0
    mov    TL0,    #0            ; Заново занесемо значення в рахунковий регістр Timer0
    mov    TH0,    #0
    mov    KeyPort,#0            ; Всі ніжки скидаємо в 0
    nop                    ; Потупимо 1 мкс, щоб на ніжках встановилося значення
    mov    KeyPort,#0FFh            ; Всі ніжки встановлюємо в 1
    setb    TR0                ; запустимо Timer0
Again:
    mov    A,    KeyPort            ; зчитуємо стан сенсорів
    cjne    A,#0FFh,NoPush            ; Якщо не всі зарядилися, значить не було натискання (натиснутий сенсор зарядиться останнім)
    clr    TR0                ; Всі зарядилися, зупинимо Timer0
    jmp    GetPush
NoPush:
    mov    CodeSensor,A            ; Збережемо поточний стан сенсорів, щоб знати, який зарядився останнім
    jmp    Again                ; І йдемо перевіряти знову
GetPush:
    mov    A,    TH0
    cjne    A,#SensorH,Push_Or_No        ; Порівняємо час задяду останнього сенсора зі зразком (старший байт)
    mov    A,    TL0            ; Таке ж, значить треба перевірити молодший байт попугаїв
    cjne    A,#SensorL,Push_Or_No
    jmp    No                ; І молодший байт такий же, значить не було торкання сенсора, адже має бути більше
Push_Or_No:
    jc    No                ; Менше, значить не було торкання сенсора, адже має бути більше
        clr    zumer                ; Було торкання. Озвучимо дотик до сенсора
    setb    F0                ; Обробка натискання сенсора, встановимо флаг, який дає зрозуміти, що було торкання сенсора
    jmp    TouchPad_heandler        ; Перейдемо на обробник торкання сенсора
No:                        ; Значить останній сенсор зарядився сам по собі, і ніхто до нього не торкався
    mov    CodeSensor,#0FFh        ; Тому занесемо в змінну сенсора одиниці
    jmp    End_TouchPad_heandler

TouchPad_heandler:                ; Обробник натиснення сенсора
    mov    A,    CodeSensor        ; Занесемо код натиснутого сенсора
    cjne    A,#11111110b,Sensor_2        ; І порівняємо з першим сенсором
    mov    symbol,#41h            ; Це він
    jmp    End_TouchPad_heandler        ; Ну і пропускаємо подальшу перевірку
Sensor_2:                    ; Це не 1 сенсор, перевіряємо 2, і т.д.
    mov    A,    CodeSensor
    cjne    A,#11111101b,Sensor_3
    mov    symbol,#42h
    jmp    End_TouchPad_heandler
Sensor_3:
    mov    A,    CodeSensor
    cjne    A,#11111011b,Sensor_4
    mov    symbol,#43h
    jmp    End_TouchPad_heandler
Sensor_4:
    mov    A,    CodeSensor
    cjne    A,#11110111b,Sensor_5
    mov    symbol,#44h
    jmp    End_TouchPad_heandler
Sensor_5:
    mov    A,    CodeSensor
        cjne    A,#11101111b,Sensor_6
    mov    symbol,#45h
    jmp    End_TouchPad_heandler
Sensor_6:
    mov    A,    CodeSensor
        cjne    A,#11011111b,Sensor_7
    mov    symbol,#46h
    jmp    End_TouchPad_heandler
Sensor_7:
    mov    A,    CodeSensor
        cjne    A,#10111111b,Sensor_8
    mov    symbol,#47h
    jmp    End_TouchPad_heandler
Sensor_8:
    mov    A,    CodeSensor
        cjne    A,#01111111b,End_TouchPad_heandler
    mov    symbol, #48h

End_TouchPad_heandler:
    call    Del_100ms            ; затримка
    mov    TH0,    #4Ch            ; Заново занесемо значення в рахунковий регістр Timer0
    mov    TL0,    #0h            ; При цьому значенні переривання виникає кожні 50 мс
    setb    EA                ; Дозволяємо все переривання
    setb    TR0                ; запускаємо Timer0
    reti

Del_100ms:
    mov    R1, #0FFh
    mov    R0, #0FFh
    djnz    R0, $
    djnz    R1, $-4
        ret
end

Писав я його давненько, і зараз, дивлячись на нього, бачу, що можна було написати набагато краще, але публікую як є, може комусь і таке згодиться.
Одразу кажу, що на мікроконтролерах даної архітектури можливо зробити сенсорну клавіатуру лише використовуючи порт Р0, бо він, при звичайному режимі роботи, не має вбудованих підтягуючих резисторів, які б шунтували наші зовнішні мегаомні резистори. У АВР-ах наприклад ці вбудовані підтягуючі резистори є на всіх портах, але там їх можна відключати.
Виглядає це все діло ось так:

Post's attachments

100_46051.jpg 120.97 kb, 162 downloads since 2016-03-18 

Подякували: 0xDADA11C7, leofun01, raxp3

26 Востаннє редагувалося raxp (06.04.2016 20:09:52)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

До посту #23 підкину ще один варіант розширення портів мінімальними засобами - чарліплексування. Є така фірма-виробник Maxim (драйвери MAX232 адже всім знайомі), так ось працював у них такий Аллен Чарлі. І 20 років тому подумав він, подумав і вирішив використовувати біполярні властивості сучасних драйверів в МС. По суті метод зводиться до комутації шляхів для струму. Отже...

Варіант для керування 6-ю світлодіодами трьома портами:
http://ipic.su/img/img7/fs/Screen1.1459965850.png

Зворотний варіант iз клавіатурою:
http://ipic.su/img/img7/fs/Screen0.1459965833.png

У класичному матричному включенні на це треба було б: 2х3 лінії, або тупо 6.

Подякували: MCS-51, 0xDADA11C7, ReAl3

27 Востаннє редагувалося MCS-51 (18.04.2016 21:47:53)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

А у мене прогрес йде потихеньку. У ході розробки своїх проектів почав помічати поступове збільшення "кількості коду" із кожним новим проектом. Той проект що роблю зараз, уже займає близько 1000 строк коду, а це моїм підрахункам тільки половина, від того що має бути. А вже важко читається. Почав шукати як правильно будувати свої програми. І ось до чого дійшов. Якщо взяти спрощену класифікацію, то є наступні щаблі еволюції програміста-розробника мікроконтролерних систем:
1. Суперцикл
2. Суперцикл+переривання
3. Флаговый автомат
4. Диспетчер
5. Пріорітетний диспетчер
6. Кооперативна RTOS
7. Витісняюча RTOS

Донедавна я знаходився на 2 сходниці еволюції)), але як написав вище, це почало стримувати мене.
Розкуривши флаговий автомат я здавалося б знайшов щастя, тим більше що архітектура 8051 ідеально підходить для цього підходу (пробачте за тавтологію). Але цікавість взяла своює, і я поперся далі, і за кілька вечорів написав свій власний диспетчер (майже зі всім, що казав Бендер). Поки що правда без пріорітетів, але може колись прикручу. Проект складається із 2 файлів: файлу власне диспетчеру, та файлу таймерної служби. На додачу я виділив маленький файл включення для макросів загального вжитку, який планую тепер поступово наповнювати та ліпити до кожного проекту. У даному проекті диспетчер незалежно мигає 8 світлодіодами (кожним зі своєю частотою). Дискретність часових інтервалів = 1 мс. Точність трохи залежить від завантаженості, але зараз при 8 задачах вкладається у 1 відсоток. Проект носить назву "Nedo_OS" і наразі має версію 1.9 (починається із 1.0). Таймерна служба розвивається окремо і уже доросла до версії 1.5)))

Прихований текст

Nedo_OS_v1.9.asm

; Тут немає прямої передачі управленння між блоками. Все робиться через диспетчер. Потрібно запустити задачу - ми суємо її номер (по суті - адресу мітки) у чергу.
; Після виконання поточної задачі, перед передачею управління диспетчеру, вся черга просувається на 1 ступінь догори.
; І так триває до тих пір, поки не виберемо всі задачі з черги. Черга задач росте назустріч стеку, тому її довжину я не обмежував, але при великій кількості задач у черзі (>100), можливе затирання стеку, якщо і він у цей момент буде великим, майте це на увазі. 

Switch_Task    macro
        mov    DPTR,    #Task                ; беремо адресу таблиці задач
        mov    Current_Head,    @Head            ; беремо номер задачі з голови черги
        mov    A,    Current_Head
        rl    A                    ; множимо на 2, бо адреса у памяті програм скл. з 2 байт (слова)
        movc    A,    @A+DPTR                ; дістаємо 1 байт адреси
        mov    Task_addr_hi,A                ; і зберігаємо у тимчасовій комірці
        mov    A,    Current_Head            ; відновлюємо вказівник голови у АСС, бо ми його щойно зіпсували
        rl    A                    ; множимо на 2, бо адреса у памяті програм скл. з 2 байт (слова)
        inc    A                    ; збільшуємо на 1, бо нам треба 2 байт
        movc    A,    @A+DPTR                ; дістаємо 2 байт адреси
        mov    DPL,    A
        mov    DPH,    Task_addr_hi            ; Завантажуємо обидва байти адреси у DPTR
        clr    A                                       ; очистимо АСС перед стибком
        jmp    @A+DPTR                    ; і стрибаємо на нашу задачу)))))))
        endm
    
Set_Task_ms    macro    Num_Task,Delay                ; Постановка задачі із затримкою, після закінчення затримки задача буде поставлена в кінець черги
        mov     Num_of_Task,#Num_Task
        mov     Delay_Task_low,#low(Delay)
        mov     Delay_Task_hi,#high(Delay)+1
        call    Set_Task_Routine
        endm

Set_Task        macro   Num_of_Task                             ; Постановка звичайної задачі в конець черги
        clr     TR0
        clr     EA
        mov     @Tail,    #Num_of_Task
        dec     Tail
        setb    EA
        setb    TR0
        endm
        
Set_SuperTask    macro   Num_of_Task                ; Постановка термінової задачі в початок черги, виконується всередині іншої задачі, в останню чергу
        mov     @Head,    #Num_of_Task                    ; Не можна ставити задачу, у якій знаходимося, тому що зациклимося
        jmp    Dispetcher
        endm
        
Set_SuperTask_in_Interrupts    macro    Num_of_Task        ; Постановка термінової задачі в початок черги у перериванні. Дозволяє закінчитися задачі, яка була перервана. Потребує доопрацювання!!!
        push    1h                                  ; Збережемо вказівник хвоста
        push    ACC
        push    PSW
Move_Queue_Down:                        ; посуваємо чергу задач донизу
        inc     Tail                    ; зробимо адресу задачі, яку пересуваємо
        mov     A,      Tail                ; занесемо адресу задачі в Асс
        xrl     A,    #Head_Pointer            ; наложимо маску розпізнавання голови черги
        jz    Zero_2                              ; дісталися голови черги
        mov     A,      @Tail                ; не дісталися до голови черги, номер задачі у Асс
        dec     Tail                    ; посунемо вказівник донизу
        mov     @Tail,    A                ; перепишемо задачу за новою адресою
        inc     Tail                    ; і повернемося назад
        jmp    Move_Queue_Down                ; і знову пішли брати наступну задачу
Zero_2:
        dec     Tail                    ; ми досягли голови, перебір, посунемося на 2 комірку
        mov    @Tail,    #Num_of_Task            ; запишемо номертермінової задачі у 2 комірку
        pop     PSW
        pop     ACC
        pop     1h                                  ; відновимо вказівник хвоста
        dec     Tail                    ; ми додали 1 задачу до черги, посунемо хвіст
        endm

Head_Pointer    equ     0FFh                    ; адреса голови черги задач, із якої дістається номер задачі, що буде виконуватися. Щоб відвести під стек якомога більше місця, назначаємо в останню комірку ОЗП (і тоді черга задач буде рости назустріч стеку)
LED_Port    equ     P0
LED_1        equ    P0.0
LED_2        equ    P0.1
LED_3        equ    P0.2
LED_4        equ    P0.3
LED_5        equ    P0.4
LED_6        equ    P0.5
LED_7        equ    P0.6
LED_8        equ    P0.7
Buzzer          equ     P3.5

Delay_Task_hi    equ    6h
Delay_Task_low    equ     5h
Num_of_Task    equ     4h
Current_Head    equ    3h
Task_addr_hi    equ     R2
Tail            equ     R1                                      ; Tail Pointer
Head        equ     R0                                      ; Head Pointer

bseg    at      0
flag_KeyScan:    dbit    1

dseg    at      3Fh
Stack:

cseg    at      0
    jmp    Ini                                     ; jump to main cycle
org    03h
    jmp     Int_0
;    reti                                            ; Int0 interrupt
org    0Bh
    jmp    Timer0                        ; Timer0 interrupt
org    13h
    reti                                            ; Int1 interrupt
org    1Bh
    reti                                            ; Timer1 interrupt
org    23h
    reti                                            ; UART interrupt
org    2Bh
    reti                                            ; Timer2 interrupt
org    30h
;====== Include ================================================================================================================
include 'Soft_Timers_v1.5.asm'
;====== Int0 heandler ==========================================================================================================
Int_0:
    clr     TR0
    clr     EX0
    clr     IE0
    push    ACC
    push    PSW
    Set_Task_ms    9,100
    pop     PSW
    pop     ACC
    setb    TR0
    reti
;====== Main ====================================================================================================================
Ini:
    mov     SP,    #Stack
    mov     Head,   #Head_Pointer
Clear_Memory:
    mov     @Head,    #0
    djnz    Head,   Clear_Memory
 ;============== Налаштування таймерів и маски переривань ==========================================================================
    mov    TMOD,    #00000001b    ; Режим роботи таймерів Т0 и Т1
    ;         ||||||||
    ;         ||||||++-------Режим роботи Т0 (01 - 16-біт, 10 - 8-біт із автоперезавантаженням, 11 - 2 незалежних 8-біт)
    ;         |||||+---------С/Т0: 0-таймер, 1-счетчик
    ;         ||||+----------Gate_Т0: 0-управління по TR0, 1-управління по TR0 и INT0
    ;         ||++-----------Режим роботи Т1 (01 - 16-біт, 10 - 8-біт із автоперезавантаженням, 11 - неможливо)
    ;         |+-------------С/Т1: 0-таймер, 1-счетчик
    ;         +--------------Gate_Т1: 0-управління по TR1, 1-управління по TR1 и INT1

    mov    TCON,    #00010000b      ; Управління таймерами Т0 и Т1
    ;         ||||||||
    ;         |||||||+-------IT0 - Тип INT0: 0-по рівню, 1-динамічний по спаду
    ;         ||||||+--------IE0 - Запрос переривання INT0 (при IT0=1 автоматично скидається в обробнику)
    ;         |||||+---------IT1 - Тип INT1: 0-по рівню, 1-динамічний по спаду
    ;         ||||+----------IE1 - Запрос переривання INT1 (при IT1=1 автоматично скидається в обробнику)
    ;         |||+-----------TR0 - Запуск Т0
    ;         ||+------------TF0 - Флаг переповнення Timer0
    ;         |+-------------TR1 - Запуск Т1
    ;         +--------------TF1 - Флаг переповнення Timer1

    mov    IE,    #10000011b      ; Переривання
    ;         ||||||||
    ;         |||||||+-------EX0 - дозвіл обробоки переривань від зовнішнього входу INT0
    ;         ||||||+--------ЕТ0 - дозвіл обробоки переривань від Timer0
    ;         |||||+---------EX1 - дозвіл обробоки переривань від зовнішнього входу INT1
    ;         ||||+----------ЕТ1 - дозвіл обробоки переривань від Timer1
    ;         |||+-----------ES - дозвіл обробоки переривань від посл.порта
    ;         ||+------------ET2 - дозвіл обробоки переривань від Timer2
    ;         |+-------------Резерв, не використовується
    ;         +--------------ЕА - дозвіл обробоки усіх переривань
    mov     Head,    #Head_Pointer
    mov     Tail,    #Head_Pointer
    mov    LED_Port,#00000000b
    mov     TH0,    #TH_def
    mov     TL0,    #TL_def
Start_Task:
    Set_Task        1
    Set_Task        2
    Set_Task        3
    Set_Task        4
    Set_Task        5
    Set_Task        6
    Set_Task        7
    Set_Task        8
;    Set_Task        9

Dispetcher:
    Switch_Task
Idle_0:
    setb    flag_KeyScan                    ; start Idle Task
    jmp    Dispetcher
;====== Task Section ========================================================================================================
Task_1:
;    cpl     Buzzer                        ; do Task_1
    cpl    LED_1
    Set_Task_ms    1,500                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_2:                                ; do Task_2
    cpl    LED_2
    Set_Task_ms    2,1000                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_3:                                ; do Task_3
    cpl    LED_3
    Set_Task_ms    3,2000                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_4:                                ; do Task_4
    cpl    LED_4
    Set_Task_ms    4,4000                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_5:                                ; do Task_5
    cpl    LED_5
    Set_Task_ms    5,8000                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_6:                                ; do Task_6
    cpl    LED_6
    Set_Task_ms    6,16000                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_7:                                ; do Task_7
    cpl    LED_7
    Set_Task_ms    7,32001                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_8:                                ; do Task_8
    cpl    LED_8
    Set_Task_ms    8,64001                    ; Delay(ms), Num of Next Task
    jmp    Move_Queue
Task_9:                                ; do Task_9
    cpl     Buzzer
    setb    EX0
;    Set_Task_ms    9,500
    jmp    Move_Queue
;====== Посуваємо чергу задач ================================================================================================
Move_Queue:
    dec    Head                                            ; беремо наступну задачу
    mov    A,    @Head
    jz    Zero
    inc    Head                                            ; не 0, перемістимо вказівник голови на попередню позицію
    dec    Head                                            ; повернемось назад
    jmp    Move_Queue                                      ; і знову пішли брати наступну задачу
Zero:                                                           ; немає задачі
    inc    Tail                                            ; посунемо вказівник хвоста на 1 вгору
    mov    @Tail,    #0                                      ; і обнулимо цю комірку
    mov    Head,   #Head_Pointer                ; а голову повернемо на місце
    jmp    Dispetcher
;====== Додаємо задачу із затримкою =========================================================================================
Set_Task_Routine:
    bank_1
    mov     R3,     #Num_of_Timers                      ; занесемо в лічильник кількість таймерів
    mov     R0,     #Timer_1_Task                       ; беремо адресу задачі 1 таймера
Next_Timer_:
    mov    A,      @R0                                 ; беремо номер задачі цього таймеру
    jz    Set_Timer_                    ; якщо пустий, запишемо до нього
    dec    R0                                          ; інакше пропускаємо задачу цього таймера
    dec    R0                                          ; і молодший байт
    dec    R0                                          ; і старший байт
    djnz    R3,     Next_Timer_                         ; і беремо наступний таймер
    jmp    No_Set_Task                                 ; немає вільних таймерів, задача не буде поставлена до черги
Set_Timer_:
    mov    @R0,    Num_of_Task                ; запишемо номер задачі
    dec    R0
    mov     @R0,    Delay_Task_low                ; запишемо молодший байт таймеру low(Delay)
    dec    R0
    mov    @R0,    Delay_Task_hi                ; запишемо старший байт таймеру high(Delay)+1
No_Set_Task:
    bank_0
    ret
;====== Table Task ============================================================================================================
Task:                                                           ; задачі
    dw    #Idle_0
    dw      #Task_1
    dw      #Task_2
    dw      #Task_3
    dw      #Task_4
    dw      #Task_5
    dw      #Task_6
    dw      #Task_7
    dw      #Task_8
    dw      #Task_9
end
Прихований текст

Soft_Timers_v1.5.asm

; Timer0 сконфігурований таким чином, щоб генерувати системні тіки с періодом 1мс
include 'Macro.inc'

Frequency    equ    12000
Koeficient    equ    12000/Frequency
Timer_Period    equ     64542*Koeficient        ; for 1 ms
TH_def          equ     high(Timer_Period)
TL_def          equ     low(Timer_Period)
Num_of_Timers    equ    9                           ; кількість софт-таймеров, у реалі десь до 15 шт, більше не пробував, не було потреби
Timer_1_Task    equ    3Fh                             ; адреса задачі 1 таймеру, наступна адреса - молодший байт таймеру, наступна - старший байт таймеру, наступна - адреса задачі 2 таймеру і т.д. Кажна наступна адреса - на 1 менша за попередню
Timer        equ     7h                ; R7 0 банка
Timers_Counter  equ     R2                              ; R2 1 банка
Timer_Pointer   equ    R1                              ; R1 1 банка
Tail_           equ     R1                              ; R1 0 банка

cseg
Timer0:                                                 ; Timer0 interrupt heandler
    mov    TH0,    #TH_def                         ; перезавантажимо таймер
    mov    TL0,    #TL_def
    push    ACC
    push    PSW                                     ; збережемо робочий банк памяті
    bank_1
    mov    Timers_Counter,#Num_of_Timers        ; завантажимо коількість софт-таймерів
    mov    Timer_Pointer,#Timer_1_Task        ; завантажимо адрес задачі 1 софт-таймеру
Next_Timer:
    mov    A,    @Timer_Pointer                  ; завантажимо в АСС номер задачі цього софт-таймера
    jz    Skip_Set_Task                           ; якщо 0 - пропускаємо цей софт-таймер
    dec    Timer_Pointer                           ; виберемо молодший байт цього софт-таймеру
    mov    Timer,    @Timer_Pointer            ; завантажимо молодший байт софт-таймеру для перевірки
    djnz    Timer,    Skip_Set_Task_2            ; перевіряємо, і якщо не ноль - пропускаємо встановлення флагу цього таймеру
    mov    @Timer_Pointer,Timer            ; оновимо молодший байт софт-таймеру
    dec    Timer_Pointer                ; молодший байт таймеру = 0, вибираємо старший байт
    mov    Timer,    @Timer_Pointer            ; завантажимо старший байт софт-таймеру для перевірки
    djnz    Timer,    Skip_Set_Task_3            ; перевіряємо, і якщо не нуль - пропускаємо встановлення флагу цього таймеру
    bank_0
    mov    @Tail_,    A                ; завантажимо в кінец черги номер задачі цього софт-таймеру
    dec    Tail_                    ; посунемо хвіст черги для наступної задачі
    bank_1
    inc    Timer_Pointer                           ; повернемо комірку номера задачі
    inc    Timer_Pointer
    mov    @Timer_Pointer,#0            ; і обнулимо номер задачі
Skip_Set_Task:
    dec    Timer_Pointer                           ; пропускаємо молодший байт цього софт-таймеру
    dec    Timer_Pointer                           ; пропускаємо старший байт цього софт-таймеру
Next_Timer_2:
    dec     Timer_Pointer                           ; беремо наступний софт-таймер
    djnz    Timers_Counter,Next_Timer        ; перебор усіх софт-таймерів
    pop    PSW                                     ; відновимо банк памяти
    pop     ACC
    reti
Skip_Set_Task_2:
    mov     @Timer_Pointer,Timer            ; оновимо младший байт софт-таймеру
    dec     Timer_Pointer                           ; пропускаємо старший байт цього софт-таймеру
    jmp     Next_Timer_2
Skip_Set_Task_3:
    mov     @Timer_Pointer,Timer            ; оновимо старший байт софт-таймеру
    jmp     Next_Timer_2
end
Прихований текст

Macro.inc

; стрічка include 'Macro.inc' обявляєтся у першому по рахунку файлі включення (Soft_Timers_v1.5.asm)

bank_0    macro
    anl    PSW,    #11100111b
    endm

bank_1    macro
    setb    PSW.3
    clr     PSW.4
    endm

bank_2    macro
    clr     PSW.3
    setb    PSW.4
    endm

bank_3    macro
    orl    PSW,    #00011000b
    endm
Подякували: 0xDADA11C7, raxp2

28

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Дякую, мені надзвичайно приємно стежити за вашою еволюцією.

29 Востаннє редагувалося ReAl (28.04.2016 22:52:55)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

MCS-51 написав:

Інколи буває так, що кнопок потрібно більше 3-4, а виводів більше немає. І клавіатуру матричну не зробиш,
...
Схема підключення 74HC165 прикріплена

На цій же 165-тій можна і матричну :-)
Просто додати три (для 3*5=15-ти кнопок) або 4 (для 4*4=16-ти кнопок) транзистори.
Опитувати регістр так само, але потім декодувати.

А найхардкорніший варіант клавіатури до 64 кнопок на одну ніжку мікроконтролера, який я бачив (але сам не робив ;-) ) — це мікросхема від телевізійного пульта, але вихід через транзистор напряму на мікроконтролер, на тій же платі трасовано. Без інфрачервоного світлодіода та TSOP17xx.

Post's attachments

kbd-165.png 11.98 kb, 158 downloads since 2016-04-28 

Подякували: 0xDADA11C7, MCS-512

30

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

До речі, про Keil. У частині C-компілятора у нього є така гарна річ, як збирання статистики по використанню регістрів, точніше, по зміні їх вмісту. Компілятор до кожної функції прикладає перелік регістрів, які безпосередньо вона змінює. Лінкер будує дерево викликів і збирає маски нагору, видає файл, у якому для кожної функції 16-бітна маска модифікованих регістрів з урахуванням вкладених підпрограм. На наступному кроці компіляції враховується цей файл для оптимізації розміщення тимчасових змінних по регістрах.
У змішаних проектах для власних підпрограм на асемблері спеціальною директивою можна задати ці переліки модифікованих регістрів.

У результаті для

    nibble_to_ascii(buf, len);
    print(buf, len);

може скластися так, що компілятор аргументи buf та len занесе у регістри для передавання параметрів один раз, а потім поставить підряд два виклики функцій. На 10…20-кілобайтній прошивці на 300-500 байтів економії може набігти.

51-ше ядро досі живе в Silabs-сівських контролерах та Cypress-івських USB-шних FX-чіпах, то ця тема ще може бути цікавою.
Сам я вже років 12-15 не маю справ з MCS-51, але якщо комусь треба, то можу поритися по старих дисках і спробувати згадати достатньо, щоб написати рекомендації.

Подякували: MCS-511

31 Востаннє редагувалося MCS-51 (16.06.2016 15:05:14)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Доброго дня. Я люблю прості речі, тому не міг обійти стороною спосіб вводу інформації за допомогою енкодера.
Виконуючи побажання корифеїв форуму, даю посилання на Вікіпедію
https://uk.wikipedia.org/wiki/%D0%94%D0 … 1%82%D1%83
Нас цікавить найпростійший варіант - механічний інкрементний енкодер із вихідним кодом Грея https://uk.wikipedia.org/wiki/%D0%9A%D0 … 0%B5%D1%8F

Сьогодні розглянемо найпростійший спосіб обробки інформації від енкодера - у зовнішньому перериванні.
Алгоритм роботи при цьому заключається у тому, що ми підключаємо один із каналів енкодера до зовнішнього переривання, і при його спрацюванні всередині обробника переривання дивимось на стан другого каналу, і на основі цього робимо висновок про напрям обертання.
Схема намальована не мною, а взята з мережі. Можливе підключення як із зовнішнімі підтягуючими резисторами (надійніше), так із використанням внутрішніх (простіше). Конденсатори потрібні для апаратного подавлення брязкоту контактів енкодера, їх номінал близько 0,1 мкФ (100 нФ), більше не потрібно, бо при розряді конденсаторів великої ємності будуть псуватися контакти енкодера. А взагал без конденсаторів даний алгоритм працює дуже погано, є дуже багато хибних спрацювань, майте це на увазі. Номінал підтягуючих резисторів, якщо вони використовуються, від 4,7 до 100 кОм.

Прихований текст
Ch_A    equ     P3.2            ; Канал А, на ньому висить переривання, тому ми його ніколи не перевіряємо
Ch_B    equ     P3.3            ; Канал B, його перевіряємо при спрацюванні каналу А і таким чином робимо висновок про напрям обертання енкодера
leds    equ     P0            ; Порт світлодіодів, запалюються низьким логічним рівнем

cseg    at      0
    jmp     Main
org     03h
    jmp     INT_0            ; Перехід до обробки зовнішнього переривання 1
org     30h
INT_0:
    jnb     Ch_B,Action_2
Action_1:
    jb    P0.7,End_INT0        ; Якщо досягли краю світлодіодної лінійки, запинимось
    rl    A
    jmp     End_INT0
Action_2:
    jb    P0.0,End_INT0           ; Якщо досягли краю світлодіодної лінійки, запинимось
    rr      A
End_INT0:
    reti

Main:
;============== Налаштування таймерів и маски переривань ==========================================================================
    mov    TMOD,    #00000000b    ; Режим роботи таймерів Т0 и Т1
    ;         ||||||||
    ;         ||||||++-------Режим роботи Т0 (01 - 16-біт, 10 - 8-біт із автоперезавантаженням, 11 - 2 незалежних 8-біт)
    ;         |||||+---------С/Т0: 0-таймер, 1-счетчик
    ;         ||||+----------Gate_Т0: 0-управління по TR0, 1-управління по TR0 и INT0
    ;         ||++-----------Режим роботи Т1 (01 - 16-біт, 10 - 8-біт із автоперезавантаженням, 11 - неможливо)
    ;         |+-------------С/Т1: 0-таймер, 1-счетчик
    ;         +--------------Gate_Т1: 0-управління по TR1, 1-управління по TR1 и INT1

    mov    TCON,    #00000001b      ; Управління таймерами Т0 и Т1
    ;         ||||||||
    ;         |||||||+-------IT0 - Тип INT0: 0-по рівню, 1-динамічний по спаду
    ;         ||||||+--------IE0 - Запрос переривання INT0 (при IT0=1 автоматично скидається в обробнику)
    ;         |||||+---------IT1 - Тип INT1: 0-по рівню, 1-динамічний по спаду
    ;         ||||+----------IE1 - Запрос переривання INT1 (при IT1=1 автоматично скидається в обробнику)
    ;         |||+-----------TR0 - Запуск Т0
    ;         ||+------------TF0 - Флаг переповнення Timer0
    ;         |+-------------TR1 - Запуск Т1
    ;         +--------------TF1 - Флаг переповнення Timer1

    mov    IE,    #10000001b      ; Переривання
    ;         ||||||||
    ;         |||||||+-------EX0 - дозвіл обробоки переривань від зовнішнього входу INT0
    ;         ||||||+--------ЕТ0 - дозвіл обробоки переривань від Timer0
    ;         |||||+---------EX1 - дозвіл обробоки переривань від зовнішнього входу INT1
    ;         ||||+----------ЕТ1 - дозвіл обробоки переривань від Timer1
    ;         |||+-----------ES - дозвіл обробоки переривань від посл.порта
    ;         ||+------------ET2 - дозвіл обробоки переривань від Timer2
    ;         |+-------------Резерв, не використовується
    ;         +--------------ЕА - дозвіл обробоки усіх переривань

    mov     A,#11110111b    ; Виводимо на світлодіоди початкове значення - 1 запалений світлодіод у центрі лінійки
Loop:
    mov     Leds,A        ; У циклі виводимо оновлений стан на світлодіоди
    jmp     Loop
end

У даному прикладі я просто ганяю туди-сюди вогник по світлодіодній лінійці, але практичне застосування залежить лише від Вашої фантазії: контроль положення крокового двигуна, пересування пунктами меню на LCD, банальний лічильник обертів тощо.

Post's attachments

scheme.gif 19.81 kb, 139 downloads since 2016-06-16 

Подякували: 0xDADA11C71

32 Востаннє редагувалося MCS-51 (19.03.2020 23:06:12)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Хоча вже більше граюся із С та АРМами.
Простенька асмоліба для світлодіодної матриці 16*32 (P10 led matrix panels 16x32). На всяких ОЛХ по 250-300 грн, якщо ліньки чекати із Китаю.
Відео для наочності.
https://www.youtube.com/watch?v=uJ6eXrHAV2w

П.С. Робіть, і у вас все вийде.

Post's attachments

P10_LED_Library.zip 200.19 kb, 28 downloads since 2020-03-19 

Подякували: 0xDADA11C71

33 Востаннє редагувалося MCS-51 (13.08.2020 13:10:33)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Доброго часу доби. Викладаю свої простенькі "бібліотеки" для радіокомплекту із передавача FS1000A та приймача XY-MK-5V. Посилання на перше, що надибав на AliExpress: https://www.aliexpress.com/i/32960644995.html
З комплекту бібліотек зібрано 2 проекти, які являють собою міст UART-радіоканал-UART.
Вся система апаратно сладається із 2 модулів, 1 із передавачем, і 1 з приймачем.
Швидкість UART, як приймальної, так і передавальної частини, налаштовується окремо, що дає можливість створити систему, яка крім передачі даних через радіоканал буде ще й конвертувати швидкість: приймати дані по UART наприклад на швидкості 57600 бод, а віддавати на 9600 бод, або навпаки.
Я приципово поставив перед собою "спортивне" завдання створити бібліотеки для найменш потужних 8051 мікроконтролерів, тому у проекті використовується AT89C1051. Це найменш потужна, відома мені версія 8051. Вона має лише 1 КБ пам'яті програм, та 64 байти ОЗП, відсутній апаратний UART (тому був написаний програмний, і включений у кожний проект як окрема бібліотека) Самі бібліотеки (приймача і передавача) займають пару сотень байт, ще пару сотень байт займає програмний UART, так що місця для вашого коду ще лишилося. Єдине, що майте на увазі, я виділив під буфер максимально можливий для приймача розмір памяті - 48 (із 64) байт, декілька байт знадобилося для роботи самих бібліотек приймача/передавача та програмного UART, і ще декілька байт лишилося для стеку. Все прораховано до 1 байту, у тому якщо захочете дописати щось своє - зменшуйте розмір буферу на необхідну вам кількість ОЗП.
Під час розробки системи з настільки обмеженими ресурсами стикнувся з кількома проблемами, деякі з яких було успішно вирішено:

  1. Для роботи приймача необхідні флаги, але навіть 1 флаг із бітового сегменту даних призводить до втрати цілого байту ОЗП, і через свою адресацію автоматично призводить до фрагметаціїї наявної пам'яті на 2 шматки (до флагу, і після), що є дуже незручним, особливо при загальному обсязі памяті у 64 байти.
    Було вирішено обмежетися 2 флагами (ознака успішного прийому преамбули, та ознака успішного прийому всього пакету), обидва розміщені у регістрі стану системи PSW. Один загальновідомий флаг F0 (PSW.5), інший - PSW.1, майже ніколи ніким не використовується, недокументований, і автори деяких підручників не радять його використовувати. Я використав, і проблем не помітив. Використання 2 цих флагів у приймачі вмикається опцією USE_PSW_BITS. Можете вимкнути її, але це обмежить максимальний розмір прийомного буферу до 31 байту (для АТ89С1051).

  2. Програмний UART погано працює на прийом при дуже високих швидкостях. Максимальна швидкість при 12 МГц складає 57600 бод (при кварці 24 МГц можна досягти 115200 бод), але при таких швидкостях довелося вимкнути перевірку стоп-біту (старт біт перевіряється завжди), інакше система встигала прийняти лише декілька байт. У себе проблем не помітив, але хто прагне 100% надійності, обмежтеся швидкістю 38400 бод для проекту передавача, при такій швидкості перевірка стоп-біту ще увімкнена.

  3. Оскільки система перевіряє довжину кожного прийнятого біту даних на валідність, було вирішено обмежитися використанням простої checksum, а не робити CRC8. Жодних хибних прийомів не було помічено.

  4. На кварцах 22-24 МГц система не тестувалася, і теоретично, обмеженням може бути лише пропускна можливість приймача, але згідно даних звідси https://radiolis.pp.ua/arduino/49-bespr … a-mx-rm-5v
    вона складає "5 кб/сек", чого ще достатньо (при 12 Мгц наша система має приблизно 1,5 кб/сек, значить при 24 МГц стане 3 кб/сек). Це не означає, що можна кидати у передавач пакети по 1500/8=187 байт на секунду. Майте на увазі розмір буфера (максимально 48 байт для АТ89С1051), і час, необхідний приймачу, щоб видати отримане по радіоканалу, у UART (програмний, без переривань). На практиці, у мене на швидкості 57600 бод з обох кінців, успішно передається по 48 байт кожні 400 мс (десь 1/60 цього часу - видача приймачем отриманих даних по програмному UART, протягом цього часу приймач не реагує на пакети. Тому, якщо будете зменшувати швидкість програмного UART, збільшиться час між прийомами пакетів, а значить і зменшиться пропускна можливість каналу, майте це на увазі). 48*8=384 біти за 400мс, це 960 біт/сек. Майже 1 кб/сек, це фінальна швидкодія усього радіомоста при 12 МГц. При 24 Мгц можна досягти 2 кб/сек. Дальнобійність на відкритій місцевості заявлена до 100м, я у полях не перевіряв, але через 1-2 цегляних стіни дані передаються впевнено, з першого разу. Буду дуже вдячний, якщо хтось повторить проект на кварці 24 Мгц із швидкістю 115200 бод, і скаже, що все працює.

  5. Правильно взаємно розташовуйте антени передавача та приймача. Неправильно взаємне розташування антен веде до того, що якість прийому погіршується. Мною помічено, що при дуже малих відстанях (до 5 м, у межах однієї кімнати) краще взагалі без антен, ніж із неправильно розташованими антенами.

Під спойлером трохи теорії з роботи цієї системи, для того, щоб ви могли зрозуміти, чи достойне моє рішення вашої уваги.
Деякі теоретичні основи, викладені тут, висловлені не мною, і я просто використав їх під час розробки алгоритму

Прихований текст

; Варіант №1 - біт даних кодується довжиною високого рівня
; Вихід приймача підключаємо безпосередньо до P3.2 (INT0), або P3.3 (INT1)

; УВАГА !!! При утриманні рівня понад 100 мс прийом зривається. Потрібно міняти рівень не рідше 1 разу на 100 мс
; Імпульси коротше 200 мкс погано проходять через тракт приймача. Для цього комплекту оптимальний таймінг 350-1000 мкс
; Для імпульсів 50-100 мкс приймачі коштують на порядок дорожче, і це, як правило, супергетеродини
; Щодо зміни тривалостей імпульсів після проходження тракту - питання дуже велике і у 2 словах його не опишеш
; Це пов'язано з АЧХ аналогового тракту, який має завал на високому краю діапазону
; Є просте емпіричне правило: після проходження тракту короткий імпульс коротшає, довгий - подовжується
; На цьому приймачі високий рівень коротшає, а низький - подовжується
; Приймач приходить у готовність через 40-50 мс після початку пакета, а тривалість паузи між пакетами може бути 100мс
; Можна застосувати один фокус, щоб прискорити адаптацію приймача. На початку пакета передається довгий імпульс несучої 3-4мс,
; потім передається пілот з 15-20 коротких імпульсів, потім синхроімпульс, і потім вже дані
; Кожна мілісекунда несучої еквівалентна по енергії 2 мілісекундам коду, що дає можливість вдвічі скоротити час готовності

; Теорія:
; В ідеалі, приймач і передавач (якщо обидва на MCS-51) повинні працювати від однакових кварцу (бо всі обмеження по часу прив'язані до тіків Timer0)
; У зв'язку з використанням побітної синхронізації допускається використання близьких за номіналом кварців (наприклад 12 МГц для передавача, і 11,0592 МГц для приймача, або навпаки)
; Обов'язково потрібна преамбула із синхробітом. Преамбула для виходу приймача у робочий режим. Синхробіт повинен відрізняться тривалістю від біта даних (0 і 1),
; щоб якщо приймач в якийсь момент проґавив початок передачі, то він не починав прийом даних з середини пакета, а дочекався початку нової передачі
; Таким чином синхробіт дає можливість синхронізуватися, тобто визначити початок передачі інформаційних даних

; Параметри:
; Всі обмеження по часу вказані для кварцу 12МГц, але це не грає особливої ролі, так як все прив'язане до тіків Timer0. Можна взяти будь-який кварц, хоч на 24МГц, і тоді обмеження по часу зменшаться у 2 рази. Просто тоді для передавача теж доведеться брати кварц з близьким номіналом
; 0 кодується високим рівнем тривалістю від 100h до 1FFh тіків Timer0. Для 12 МГц це від 256us до 511us (при ідеальному передавачі - 384us)
; 1 кодується високим рівнем тривалістю від 200h до 2FFh тіків Timer0. Для 12 МГц це від 512us до 767us (при ідеальному передавачі - 640us)
; Біт преамбули кодується високим рівнем тривалістю від 300h до 3FFh тіків Timer0. Для 12 МГц це від 768us до 1023us (при ідеальному передавачі - 900us)
; За будь-яким високим рівнем (біта преамбули, синхробіта, або біта даних) завжди слідує низький рівень, тривалістю від 100h до 1FFh тіків Timer0. Для 12 МГц це від 256us до 511us (при ідеальному передавачі - 384us)
; Все що не вписується у дані проміжки - помилка прийому
; Біт даних до прийому синхробіта - ігнорується
; Раптовий імпульс з довжиною біта преамбули (або сінхробіта) в процесі прийому даних - помилка прийому
; Будь-яка помилка прийому переводить приймач у початковий стан - очікування сінхробіта (чергового високого рівня з тривалістю в межах від 300h до 3FFh тіків Timer0)

; Порядок роботи:
; При включенні приймача:
; Налаштовуємо 2 переривання: INT0 (або INT1), і по переповнення Timer0. Вхід в обробник переривання Timer0 - помилка прийому (зупинка Timer0, заборона зовнішнього переривання на нозі приймача). Таким чином автоматично будуть відсіюватися усі занадто довгі часові проміжки
; Заводимо Timer0 на фіксацію значення преамбули (TL0 = 0, TH0 = 0FCh), заводимо лічильник на 8 біт даних, чекаємо початку високого рівня
; Як тільки зафіксовано високий рівень - запускаємо Timer0, дозволяємо зовнішнє переривання INT0 (INT1), і переходимо до очікування в основному циклі

; В основному циклі:
; Як тільки радіоприймач видає низький рівень - викликається обробник переривання INT0 (INT1)
; Скидаємо TL0 в 0. Це дає нам 255 тактів до перезапису TH0. Цього з запасом вистачає на найдовший варіант проходження обробника

; Аналізуємо біт закінчення прийому преамбули. Якщо скинутий - обробка біта преамбули. Якщо встановлено - обробка біта даних

; Обробка біта преамбули
; Дивимося значення TH0. Якщо TH0! = 0FFh - помилка прийому
; Інакше - правильна довжина біта преамбули - виставляємо біт закінчення прийому преамбули
; Переходимо до загального для всіх оброблювачу низького рівня приймача

; Обробка біта даних
; Дивимося значення TH0. Якщо TH0 = 0FEh - це лог.0, якщо TH0 = 0FFh - це лог.1 (Це можна перевірити хитрою конструкцією: від 0FEh відняти поточний TH0. Лог.1 встановить флаг переносу, лог.0 встановить флаг нуля. Занадто короткий сигнал НЕ встановить нічого. Занадто довгий сигнал відсіємо ще раніше, перериванням по переповненню Timer0)
; Засуваємо прийнятий біт у байт даних (через АСС), декремент лічильника біт даних. Якщо став 0 - заново заводимо лічильник на 8 біт даних

; Дивимося значення лічильника прийнятих байт. Якщо 0, то приймаємо 1-й байт пакету - кількість даних, інакше приймаємо черговий байт даних, або CHECKSUM (останній байт пакету, не пишеться в приймальний буфер)

; Прийом байта кількості даних
; Дістаємо щойно прийнятий байт в АСС, і приймаємо за основу CHECKSUM, копіюємо значення з АСС в лічильник прийнятих байт, і після декремента, в змінну кількості даних
; Переходимо до загального для всіх обробника низького рівня приймача

; Прийом чергового байта даних, або checksum
; Дістаємо щойно прийнятий байт в АСС
; Декремент лічильника прийнятих байт. Якщо став 0 - закінчення прийому. Порівнюємо прийняту і пораховану CHECKSUM, якщо співпало, виставляємо біт закінчення прийому. Інакше помилка прийому
; Якщо не став 0, робимо виключне АБО (xrl) прийнятого байта з поточною CHECKSUM, пишемо прийнятий байт у буфер, інркементуємо вказівник на буфер
; Переходимо до загального для всіх обробника низького рівня приймача

; Загальний для всіх обробник низького рівня приймача
; Заводимо Timer0 на фіксацію закінчення низького рівня (TH0 = 0FEh), в TL0 вже щось натікало, але без переповнення, так що час ще є
; Очікуємо закінчення низького рівня з контролем переповнення Timer0:
; jb    TF0,    Receive_Error
; jnb    xy_mk_5v_pin,$-3
; По закінченню дивимося значення TH0. Якщо TH0! = 0FFh, помилка прийому
; Інакше продовжуємо прийом, заводимо Timer0 на фіксацію біта даних (TL0 = 0, TH0 = 0FDh)

; Структура пакету:
; 1. 1 довгий імпульс преамбули
; 2. 16-20 коротких імпульсів преамбули
; 3. 1 синхробіт
; 4. Перший байт - LEN (LEN> = 2 && LEN <= BUFF_SIZE+1) DATA + CHECKSUM
; 5. Наступні байти - DATA
; 6. Останній байт - CHECKSUM (LEN + XOR all DATA bytes)

Post's attachments

XY-MK-5V_Receiver+FS1000A_Transmitter.zip 49.35 kb, 7 downloads since 2020-08-13 

Подякували: 0xDADA11C7, ReAl2

34

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

Ви б це кудись у відкритий репозиторій на гітлабі чи що.
Бо от чесно — ліньки качати архів і розпаковувати, щоб якщо схочеться щось прокоментувати, то потім знову писати сюди з цитатами коду.

35 Востаннє редагувалося MCS-51 (16.08.2020 16:01:45)

Re: Програмування мікроконтролерів MCS-51 (Intel 8051 сумісних)

ReAl написав:

... відкритий репозиторій на гітлабі чи що...

Гітхаб підійде ? Тільки я там коменти гуглтранслейтом перевів у інгліш, але там не UTF, а ще я полюбляю таби замість пробілів, тому місцями "вирвиоко" вийшло.
https://github.com/AssmoFun/8051_projec … ransmitter

Подякували: 0xDADA11C7, ReAl2