Странное поведение 7 сегментного индикатора

Тема в разделе "Схемотехника, компоненты, модули", создана пользователем a1000, 27 апр 2019.

  1. a1000

    a1000 Гик

    Разбираясь с ассемблером дошёл до динамической индикации. Решил повторить в железе эксперимент из видео


    Приобрёл 4 символьный индикатор E40281-I-UR3-0-W, подкорректировал код под 4 символа и Atmega8
    Код (C++):
    .include "m8def.inc" // Подключаем заголовочный файл ATMega16
    .def temp = r16 // Присваиваем регистрам символические имена
    .def razr1 = r17
    .def razr2 = r18
    .def razr3 = r19
    .def sys = r20
    .def try = r21

    .equ ch1=0b10011111 // Задаем эквивалентику цифр для общего анода
    .equ ch2=0b00100101 // кстати можешь в папке этого проекта найти
    .equ ch3=0b00001101 // файлы CHOC и CHOA, я в них поместил набор
    .equ ch4=0b10011001 // символов, для простоты написания кода, для
    .equ ch5=0b01001001 // общего катода и анода соответственно
    .equ ch6=0b01000001
    .equ ch7=0b00011111
    .equ ch8=0b00000001
    .equ ch9=0b00001001
    .equ ch0=0b00000011

    .dseg // сегмент ОЗУ
    Visible: // метка Visible
    .byte 8 // по ней в ОЗУ зараезервируем 8 байт
    .cseg // Программный сегмент
    .org 0 // Адрес вектора Reset
    rjmp Reset // Переходим на Reset
    .org $009 // Адрес вектора таймера 0 по переполнению
    rjmp TIM0_OVF ; // Переходим на TIM0_OVF

    Reset: // Метка Reset
    ldi temp, high(RAMEND) // Инициализируем стек
    out sph, temp
    ldi temp, low(RAMEND)
    out spl, temp

    ldi temp, ch1
    sts Visible, temp
    ldi temp, ch2
    sts Visible+1, temp
    ldi temp, ch3
    sts Visible+2, temp
    ldi temp, ch4
    sts Visible+3, temp
    ldi temp, ch5
    sts Visible+4, temp
    ldi temp, ch6
    sts Visible+5, temp
    ldi temp, ch7
    sts Visible+6, temp
    ldi temp, ch8
    sts Visible+7, temp

    ldi temp, 0xff // Порты D и B на выход, на нем висит наш индикатор
    out DDRD, temp
    out DDRB, temp

    ldi temp, 0b00000010 // Ставим предделитель CS = 010
    out TCCR0, temp
    ldi temp, 0b00000001 // TOV0 = 1 бит прерывания по переполнению таймера 0
    out TIFR, temp
    out TIMSK, temp
    ldi temp, 0xFE
    out TCNT0, temp // А тут в тикающий регистр засовываем FE
    ldi sys, 0b10000000 // ставим начальное положение sys
    sei // Разрешаем прерывания

    Proga: // Основная программа

    rjmp Proga // Переход на Proga



    TIM0_OVF: // Прерывание по переполнению
    cli // Запрещаем прерывания
    lsr sys // Производим логический сдвиг вправо
    cpi sys, 0b00001000 // Проверяем, не ушли ли за пределы сегментов отображения на индикаторе
    breq Mary // Если ушли, переходим на обнуление т.е. метка Мэри
    out PORTD, sys // Если нет, выводим значение sys в порт
    ld temp, X+ // загрузим значение из X(Visible) в temp
    ld temp, X // загрузим значение из X(Visible) в temp
    out PORTB, temp // И выкинем на порт

    Vix: // Метка Vix
    ldi temp, 0x00 // Взводим тикающий регистр
    out TCNT0, temp
    sei // Разрешаем прерывания
    reti // Выходим из прерывания

    Mary: // Метка Мэри. Почти Поппинс =D
    ldi sys, 0b10000000 // В sys значение
    out PORTD, sys // Выводим на порт
    ldi XH, high(Visible+4) // Тут произведем зарядку 1 адреса нашего Visible в регистр косвенной
    ldi XL, low(Visible+4) // адресации, сперва старший, потом младший
    ld temp, X // загрузим значение из X(Visible) в temp
    out PORTB, temp // И выкинем на порт
    rjmp Vix // Перейдем на Vix
    При запуске получаем интересную картину. Первый разряд работает нормально, изменяются цифры от 1 до 5. На трёх остальных выводится какой-то мусор.
    Пробую вывести просто цифры. Загружаю такой код
    Код (C++):
    .include "m8def.inc" // Подключаем заголовочный файл ATMega16
    .def temp = r16 // Присваиваем регистрам символические имена
    .def razr1 = r17
    .def razr2 = r18
    .def razr3 = r19
    .def sys = r20
    .def try = r21

    .equ ch1=0b10011111 // Задаем эквивалентику цифр для общего анода
    .equ ch2=0b00100101 // кстати можешь в папке этого проекта найти
    .equ ch3=0b00001101 // файлы CHOC и CHOA, я в них поместил набор
    .equ ch4=0b10011001 // символов, для простоты написания кода, для
    .equ ch5=0b01001001 // общего катода и анода соответственно
    .equ ch6=0b01000001
    .equ ch7=0b00011111
    .equ ch8=0b00000001
    .equ ch9=0b00001001
    .equ ch0=0b00000011

    .dseg // сегмент ОЗУ
    Visible: // метка Visible
    .byte 8 // по ней в ОЗУ зараезервируем 6 байт
    .cseg // Программный сегмент
    .org 0 // Адрес вектора Reset
    rjmp Reset // Переходим на Reset
    .org $009 // Адрес вектора таймера 0 по переполнению
    rjmp TIM0_OVF ; // Переходим на TIM0_OVF

    Reset: // Метка Reset
    ldi temp, high(RAMEND) // Инициализируем стек
    out sph, temp
    ldi temp, low(RAMEND)
    out spl, temp

    ldi temp, ch1
    sts Visible, temp
    ldi temp, ch2
    sts Visible+1, temp
    ldi temp, ch3
    sts Visible+2, temp
    ldi temp, ch4
    sts Visible+3, temp
    ldi temp, ch5
    sts Visible+4, temp
    ldi temp, ch6
    sts Visible+5, temp
    ldi temp, ch7
    sts Visible+6, temp
    ldi temp, ch8
    sts Visible+7, temp

    ldi temp, 0xff // Порты D и B на выход, на нем висит наш индикатор
    out DDRD, temp
    out DDRB, temp

    //ldi temp, 0b00001101
    //out PORTB, temp

    ldi temp, 0b00000010 // Ставим предделитель CS = 010
    out TCCR0, temp
    ldi temp, 0b00000001 // TOV0 = 1 бит прерывания по переполнению таймера 0
    out TIFR, temp
    out TIMSK, temp
    ldi temp, 0xFE
    out TCNT0, temp // А тут в тикающий регистр засовываем FE
    ldi sys, 0b10000000 // ставим начальное положение sys
    sei // Разрешаем прерывания

    Proga: // Основная программа

    rjmp Proga // Переход на Proga



    TIM0_OVF: // Прерывание по переполнению
    cli // Запрещаем прерывания
    lsr sys // Производим логический сдвиг вправо
    cpi sys, 0b00001000 // Проверяем, не ушли ли за пределы сегментов отображения на индикаторе
    breq Mary // Если ушли, переходим на обнуление т.е. метка Мэри
    out PORTD, sys // Если нет, выводим значение sys в порт
    ld temp, X+ // загрузим значение из X(Visible) в temp
    ld temp, X // загрузим значение из X(Visible) в temp
    out PORTB, temp // И выкинем на порт

    Vix: // Метка Vix
    ldi temp, 0x00 // Взводим тикающий регистр
    out TCNT0, temp
    sei // Разрешаем прерывания
    reti // Выходим из прерывания

    Mary: // Метка Мэри. Почти Поппинс =D
    ldi sys, 0b10000000 // В sys значение
    out PORTD, sys // Выводим на порт
    ldi XH, high(Visible) // Тут произведем зарядку 1 адреса нашего Visible в регистр косвенной
    ldi XL, low(Visible) // адресации, сперва старший, потом младший
    ld temp, X // загрузим значение из X(Visible) в temp
    out PORTB, temp // И выкинем на порт
    rjmp Vix // Перейдем на Vix
    Все разряды работают, Нормально отображает 1 2 3 4
    Опять загружаю первый код - всё работает в норме. Ну думаю всё нормализовалось, пол первого ночи, пошёл спать.
    Утром включаю, та-же картина. Первый разряд работает нормально, изменяются цифры от 1 до 5. На трёх остальных выводится какой-то мусор. Делаю ту-же процедуру - гружу второй код, норма. Гружу первый код - опять заработало как надо.
    Кто нибудь сталкивался с такими фокусами? В чём причина неадекватного поведения индикатора?
     
  2. SergeiL

    SergeiL Гуру

    ИМХО, не тратьте время на написание на чистом ассемблере, углубитесь в Си.
    Си - это прекрасный язык, позволяющий писать код, работающий быстро и оптимально!
    А если не хватает Си, есть ассемблерные вставки в Си-шном коде.
    Все возможности Асма решаютcя легко и просто!
    А ассемблер, можно прекрасно изучать по листингам компиляции Вашего Си-шного кода.
    И на основе изучения листинга - корректировать код. :)

    Советую на основании своего опыта!
    С Асма ушел на Си и не разу, посе этого ни посмотрел в сторону чистого
    Асма!
     
    Последнее редактирование: 27 апр 2019
    AlessandrIT и b707 нравится это.
  3. a1000

    a1000 Гик

    Я не буду с вами спорить, я с вами согласен. Я не занимаюсь микроконтроллерами на профессиональном уровне, мне просто интересно. Познакомился со всем этим через ардуино. Теперь захотелось посмотреть на это всё на более глубоком уровне. Так, что если возникнет идея реализации какого-либо проекта, то писать буду в в среде ардуино.
    Вопрос по индикатору возник при рассмотрении самого принципа динамической индикации. Когда возникла необходимость в применении 7 сегментного индикатора в реальном устройстве я использовал модуль со встроенным TM1637.
     
  4. Этот индикатор - кучка светодиодов. Никакого "поведения" у него нет. Просто ваша первая программа не(адекватно) инициализирует какой-то регистр процессора. Это делает вторая программа. Потом первая нормально работает до выключения питания.
     
    Последнее редактирование: 29 апр 2019
  5. parovoZZ

    parovoZZ Гуру

    При ресете все регистры сбрасываются (кроме двух) в отличие от NVM памяти, коей является ОЗУ. Поэтому очистка ОЗУ перед инициализацией переменных - главная задача у компилятора. Как там у ASM компиляторов - ХЗ..
     
  6. a1000

    a1000 Гик

    Обе программы аналогичны. Вторую я "сделал" из первой, когда индикатор адекватно не запустился. Убрал декремент первого разряда.
     
  7. Ну не знаю. У вас там отличаются коды после метки Mary:
    Это всё, что я заметил.
     
  8. Limoney

    Limoney Гик

    Написал так, как работает, ничего странного. Ну, вот посудите, Вы считаете нормально из подпрограммы Mary резкий переход на подпрограмму Viх, не смотря на то что выход из Vix как обработки прерывания?