Alex_641, запущенный процесс преобразования работает в АЦП аппаратно независимо от того, что делает в данный момент АЛУ, и по окончании его выставляется флаг ПРЕОБРАЗОВАНИЕ ОКОНЧЕНО.
All interrupts have a separate Interrupt Vector in the Interrupt Vector table. The interrupts have priority in accordance with their Interrupt Vector position. The lower the Interrupt Vector address, the higher the priority. --- ru: Все прерывания имеют отдельный вектор прерывания в таблице векторов прерываний. Прерывания имеют приоритет в соответствии с их положением в векторе прерывания. Чем меньше адрес вектора прерывания, тем выше приоритет.
Alex_641, видимо, что-то ты не правильно делаешь. два аппаратных устройства работают независимо друг от друга и не мешают друг другу. но ты отказываешься эту независимость понимать. лично мне никогда не приходилось корректировать результат АЦП - всегда АЦП работает правильно и совершенно линейно.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
REGISTER 2-5: PIR1: PERIPHERAL INTERRUPT REQUEST REGISTER 1 ... bit 6 ADIF: A/D Interrupt Flag bit 1 = A/D conversion complete 0 = A/D conversion has not completed or has not been started ...
или без прерывания:
Код:
REGISTER 9-1: ADCON0: A/D CONTROL REGISTER 0 ../ bit 1 GO/DONE: A/D Conversion Status bit 1 = A/D conversion cycle in progress. Setting this bit starts an A/D conversion cycle. This bit is automatically cleared by hardware when the A/D conversion has completed. 0 = A/D conversion completed/not in progress ...
Вероятно, используете данные прежде чем закончилось преобразования. --- Какова компенсация выше? В коде? В некотором смысле, алгоритм?
Последний раз редактировалось veso74 Сб мар 25, 2023 20:21:53, всего редактировалось 1 раз.
Работает программа , происходит считывание АЦП, и тут .... прерывание то таймеру ... АЦП идёт накуй , само собой , прерывание же ! Да таймеры времени правильно считают время. НО а что с прерванным АЦП происходит ? Он ( АЦП ) получает неизвестно что !?
У меня такое ощущение, что Вы говорите о программном АЦП. А скажу по секрету (только чтобы об этом больше никто не знал!!!), в контроллерах бывают ещё и аппаратные АЦП. Они работают не только независимо от процессора, но даже от другого источника тактовой частоты. То есть проц работает на своей частоте, выполняет свою работу (или просто спит), а АЦП в это же самое время потихоньку на своей (скорее всего более низкой) частоте делает своё дело. Никто никому не мешает, никто ни от кого не зависит. По окончании преобразования аппаратный АЦП выставляет флаг готовности, который может быть заведён также и в систему прерываний (в зависимости от конкретного МК). Поэтому никаких коррекций при чтении данных с аппаратных АЦП не требуется.
Все бросились обсуждать, то что не в тему. Читаем название темы: Плата LED&KEY (TM1638). Чтение кнопок, ассемблер. Ничего умного, кроме предложения опроса каждые 100 мс не написано. Я очень долго бьюсь над задачей опроса именно ТМ1638, но конкретного факта нажатия ещё не выловил. Для флага нажатия хоть-какой-нибудь кнопки поставил светодиод. Для вывода кода нажатой кнопки - стоит побитный индикатор на целый порт (использую жирную PIC). Николай Савельев близко подошёл к программной реализации опроса кнопок, но слился с форума. Конкретно есть готовые примеры у кого-нибудь? Не ассемлер не предлагать и АЦП не упоминать - давайте не отходить от темы.
По программе. Пример реализации чтения с ТМ1683. Передаём команду чтения с ТМ1683, затем принимаем четыре байта. ; реализация чтения данных с клавиатуры movlw 0x42 ; чтение данных с клавиатуры call Byte_Out ; пересылка данных в ККД bsf dio_in ; RC5 на ввод call Byte_In ; принятие данных из ККД ... сохраняем первый принятый байт call Byte_In ; принятие данных из ККД ... сохраняем второй принятый байт call Byte_In ; принятие данных из ККД ... сохраняем третий принятый байт call Byte_In ; принятие данных из ККД ... сохраняем четвёртый принятый байт bcf dio_in ; RC5 на вывод
И всё. Все четыре принятых байта всегда нулевые. Кнопки естественно бешено и в беспорядке нажимаются.
;Запись в видео-память возможна в режиме: ;1) фиксированного адреса (команда 0x44): каждый раз сначала передается адрес ячейки памяти, потом байт данных ;2) автоувеличения адреса (команда 0x40): каждый раз сначала передается адрес ячейки памяти, потом несколько байт данных (до 16-ти). Каждый следующий байт пишется в следующую ячейку
;Управление включением-выключеним и яркостью задается командой: 0x80 с параметрами — битами: ;1) Влючение выключание задается установкой/сбросом 3-го бита. ;2) Яркость задается от 0 до 7 — 3-мя младшими битами.
;Запись в видео-память состоит из минимум 2-х байт: ;1) Задает адрес записи (или начала записи, если задан режим автоувеличения адреса): 0xC0+ адрес 0-15. ;2) Собственно байт данных
;Чтение клавиатуры состоит из 3-х этапов: ;1) Отправки команды чтения клавиатуры: 0x42 ;2) Не забыть отпустить DIO (setb DIO) ;3) Чтения 32 бит данных
STB equ P2.0 CLK equ P2.1 DIO equ P2.2
Byte equ R7 ; отсылаемый в ТМ1638 байт Bit_counter equ R6 ; счетчик отправленых или принятых бит Bit_counter_ equ 6h ; он же, в форме для сохранения в стек Symbol equ R5 ; порядковый номер выводимого на индикатор числа Symbol_counter equ R4 ; счетчик выводимых на индикатор чисел, до 8 KeyByte_counter equ R3 ; счетчик примаемых байт состояния клавиатуры, до 4 KeyByte_pointer equ R1 ; указатель на 1 из 4 примаемых байт состояния клавиатуры (подходит только R0 или R1) KeyByte equ 9h ; байт состояния клавиаутры, установленый байт отвечает нажатой кнопке, после него должны быть свободны еще 3 байта, в которые временно записывается состояние клавиатуры при считывании Counter equ 8h ; счетчик, показывающий порядковый номер нажатой кнопки (установленого бита в байте KeyByte) Temp_Counter equ R2 ; временная версия предыдущего счетчика, из которой делается вычетание при сравнении, и содержимое которой соответсвенно портиться при каждом сравнении Led_State equ 0h ; здесь хранится состояние светодиодов ТМ1638
dseg at10h Stack:
cseg at0 jmp Ini
org 0Bh ; Прерывание по переполнению Timer0 jmp TM1638
TM1638: push ACC push Bit_counter_ mov Byte, #01000010b ; Установка режима работы - Чтение клавиатуры ; ||| ; ||+-------- Запись в индикатор (0) / Чтение клавиатуры (1) ; |+--------- Автоинкремент адреса (0) / фиксированный адрес (1) ; +---------- Normal Mode (0) / Test Mode (1) call Send_Byte setb DIO ; Установим ногу на вход !!! mov KeyByte_counter,#4 ; Будем читать 4 байта клавиатуры mov KeyByte_pointer,#KeyByte; Установим указатель на место записи 1 считаного байта Loop_Receive_All_Keys: mov Bit_counter,#8 ; Принимаем 8 бит Loop_Receive_Byte: clr CLK ; Дергаем клок rr A ; Прием идет младшим битом вперед, поэтому двигаем вправо setb CLK ; По фронту клока на DIO получаем бит jb DIO, Set_Log_1 ; Формируем запихиваемый бит clr ACC.7 jmp Skip_Set_Log_1 Set_Log_1: setb ACC.7 Skip_Set_Log_1: djnz Bit_counter,Loop_Receive_Byte ; Пока не примем 8 бит mov @KeyByte_pointer,A ; И сохраняем inc KeyByte_pointer ; Делаем адрес для следующего байта djnz KeyByte_counter,Loop_Receive_All_Keys ; Принимаем 4 байта setb STB ; И поднимаем строб - прием закончен mov A, KeyByte+1 ; А теперь склеим все 4 байта в 1 и получим байт с состоянием всех 8 кнопок rl A orl KeyByte,A mov A, KeyByte+2 rl A rl A orl KeyByte,A mov A, KeyByte+3 rl A rl A rl A orl KeyByte,A ; Склейка байтов в 1 закончена, теперь у нас в KeyByte состояние всех 8 кнопок (но оно инверсное, то есть установленый бит отвечает нажатой кнопке!) mov Byte, #01000100b ; Вернем прежний режим работы - Запись в индикатор, но уже с фиксированным адресом, чтобы можно было что-то оперативно локально менять, не задевая другое ; ||| ; ||+-------- Запись в индикатор (0) / Чтение клавиатуры (1) ; |+--------- Автоинкремент адреса (0) / фиксированный адрес (1) ; +---------- Normal Mode (0) / Test Mode (1) call Send_Byte setb STB pop Bit_counter_ pop ACC reti
Ini: mov SP, #Stack ;============== Настройка таймеров и маски прерываний ========================== mov TMOD, #00000001b ; Режим работы таймеров Т0 и Т1 ; |||||||| ; ||||||++-------Режим работы Т0 (01 - 16-бит, 10 - 8-бит с автоперезагрузкой, 11 - 2 независимых 8-бит) ; |||||+---------С/Т0: 0-таймер, 1-счетчик ; ||||+----------: 0-управление по TR0, 1-Управление по TR0 и INT0 ; ||++---------- работы Т1 (01 - 16-бит, 10 - 8-бит с автоперезагрузкой, 11 - невозможно) ; |+----------/Т1: 0-таймер, 1-счетчик ; +----------: 0-управление по TR1, 1-управление по TR1 и INT1
mov TCON, #00010000b ; Управление таймерами Т0 и Т1 ; |||||||| ; |||||||+-------IT0 - Тип INT0: 0-по уровню, 1-динамический по спаду ; ||||||+--------IE0 - Запрос прерывания INT0 (при IT0=1 автоматически сбрасывается в обработчике) ; |||||+---------IT1 - Тип INT1: 0-по уровню, 1-динамический по спаду ; ||||+---------- - Запрос прерывания INT1 (при IT1=1 автоматически сбрасывается в обработчике) ; |||+---------- - Запуск Т0 ; ||+---------- - Флаг переполнения Timer0 ; |+---------- - Запуск Т1 ; +---------- - Флаг переполнения Timer1
mov IE, #10000010b ; Прерывания ; |||||||| ; |||||||+-------EX0 - разрешение обработки прерываний от внешнего входа INT0 ; ||||||+--------ЕТ0 - разрешение обработки прерываний от Timer0 ; |||||+---------EX1 - разрешение обработки прерываний от внешнего входа INT1 ; ||||+---------- - разрешение обработки прерываний от Timer1 ; |||+---------- - разрешение обработки прерываний от посл.порта ; ||+---------- - разрешение обработки прерываний от Timer2 ; |+----------, не используеться ; +---------- - разрешение обработки всех прерываний ;============== Настройка TM1638 ============ mov Byte, #01000000b ; Установка режима работы ; ||| ; ||+-------- Запись в индикатор (0) / Чтение клавиатуры (1) ; |+--------- Автоинкремент адреса (0) / фиксированный адрес (1) ; +---------- Normal Mode (0) / Test Mode (1) call Send_Byte setb STB mov Byte, #10001000b ; Управление яркостью индикатора ; |||| ; |||+------- Младший бит яркости ; ||+-------- Средний бит яркости ; |+--------- Старший бит яркости ; +---------- Бит включения индикатора call Send_Byte setb STB ;============================ ; Заполнение индикаторов и светодиодов TM1638 начальными значениями, необходимо, чтобы не выводился мусор, если все должно быть погашено, заполняем нулями ;============================ mov Byte, #11000000b ; Установка начального адреса - (0b -1 знакоместо дисплея, 1b - LED1, 10b -1 знакоместо дисплея, 11b - LED2 и т.д.) ; |||| ; |||+------- 1 бит адреса ; ||+-------- 2 бит адреса ; |+--------- 3 бит адреса ; +---------- 4 бит адреса call Send_Byte ; Адрес шлем без строба mov Symbol, #1 mov Symbol_counter,#8 Loop_Symbol: mov DPTR, #Symbol_Table ; Адрес таблицы в DPTR mov A, Symbol ; Номер символа в АСС movc A, @A+DPTR ; Достаем символ из таблицы mov Byte, A ; Запихиваем call Send_Byte ; и отправляем inc Symbol ; Следующий символ из таблицы символов mov Byte, #0 ; Каждый второй байт (последний бит байта) это отдельный светодиод, поэтому просто гасим их call Send_Byte djnz Symbol_counter,Loop_Symbol; Шлем 8 бит setb STB ; А после данных - строб ;============== Главный цикл ================== mov KeyByte,#0 mov Led_State,#0 ; Установим начальное состояние светодиодов - все выключены Main: ;============== Процедура преобразования порядкового номера бита в байте в число, соответствующее его порядковому номеру ================ mov A, KeyByte jz Main ; Если нет нажатыж клавиш, нечего сюда и лезть mov Counter,#0 ; Обнулим счетчик порядкового номера бита Loop: mov Temp_Counter,Counter ; Перенесем счетчик порядкового номера бита во временную переменную cjne Temp_Counter,#8,Skip ; И проверяем, все ли 8 бит были проверены jmp Main ; Да, валим в начало (в реальности попадание сюда теоретически невозможно, потому что пустой байт jz Main не пропустит, а выставлений бит кнопки выкинет из цикла до попадания сюда) Skip: inc Counter ; Нет, проверяем следующий бит rr A ; Заталкиваем его в ACC.7 jnb ACC.7, Loop ;============== Здесь мы получаем в Counter число, соответствующее порядковому номеру выставленного в KeyByte бита ==== mov DPTR, #Led_Addr_Table ; Загружаем таблицу адресов светодиодов mov A, Counter ; Порядковый номер установленого бита - это номер светодиода, состояние котрого мы будем менять movc A, @A+DPTR ; Берем адрес светодиода clr EA ; Запрещаем прерывания, чтобы сканирование клавиатуры не мешало отправлять данные mov Byte, A ; Загружаем адрес свтодиода call Send_Byte ; И отсылаем (адрес шлем без строба) ;============== Этот кусок определяет, включать, или выключать светодиод в зависимости от его предыдущего состояния ==================== mov A, Led_State ; Загрузим состояние светодиодов Loop_2: rr A ; Провернем вправо djnz Counter,Loop_2 ; Столько раз, сколько насчитали в счетчик порядкого номера установленого бита jb ACC.7,Led_Off ; Проверим, включен ли текущий светодиод (по логике, нам надо было бы проверять 0 бит, но 1 раз сдвиг мы делаем в любом случае, так что 0 бит переместится в 7, поэтому его и проверяем) mov Byte,#1 ; Выключен, будем зажигать jmp Skip_Led_Off Led_Off: mov Byte,#0 ; Включен, будем гасить Skip_Led_Off: ;=========================== call Send_Byte setb STB ; А после данных - строб mov A, KeyByte xrl Led_State,A ; Переключим состояние светодиодов setb EA jmp Main ;============== Отправка 1 байта ============= Send_Byte: mov A, Byte mov Bit_counter,#8 clr STB Loop_Send_Byte: jb ACC.0, Log_1 ; Передача идет младшим битом вперед, поэтому проверяем нулевой бит clr DIO jmp Skip_Log_1 Log_1: setb DIO Skip_Log_1: clr CLK rr A ; Передача идет младшим битом вперед, поэтому двигаем вправо setb CLK djnz Bit_counter,Loop_Send_Byte ret ;============== Таблица символов ============== Symbol_Table: db 00111111b ; 0 db 00000110b ; 1 db 01011011b ; 2 db 01001111b ; 3 db 01100110b ; 4 db 01101101b ; 5 db 01111101b ; 6 db 00000111b ; 7 db 01111111b ; 8 db 01101111b ; 9
Led_Addr_Table: db 0 db 0C1h ; Адрес 1 светодиода db 0C3h ; Адрес 2 светодиода db 0C5h ; Адрес 3 светодиода db 0C7h ; Адрес 4 светодиода db 0C9h ; Адрес 5 светодиода db 0CBh ; Адрес 6 светодиода db 0CDh ; Адрес 7 светодиода db 0CFh ; Адрес 8 светодиода end
Спасибо, посмотрю. Но вроде топикстартер не эту программу приводил?
Я не смотрел код топикстартера. В последнем сообщении темы просили код на ассемблере, я привел свой рабочий код, с комментариями. Правда писал я его лет 5 назад, когда ещё совсем зелёным был, поэтому скорее всего можно написать и лучше. Но комментарии по особенностям работы с ТМ1638 есть, так что можно разобраться.
Поэтому для клавиатуры их никто не использует. А для 8-разрядной индикации гораздо удобнее MAX7219 с SPI - готовых платок на Али полно. Там легко работает аппаратный SPI.
Жуткая «портянка», похоже, это программирование из прошлого века. Пишу, условно говоря, на современном ассемблере для AVR, использовал аппаратный SPI. В среде разработки есть встроенные команды SPI_Send и SPI_Master_Receive. Первая – посылка данных через SPI, вторая – приём данных в режиме Master. Параметр Argument указывает, что данные – аргументы. SPI_SS – вывод SS SPI, SPI_MOSI_DIR – бит вход/выход вывода MOSI. Keys – 4-хбайтная переменная, в которой хранятся байты кнопок, принятые от ТМ1638. Программа чтения кнопок получилась совсем простой: SPI_SS = 0 SPI_Send Argument &H42 SPI_MOSI_DIR = 0 SPI_Master_Receive Argument Keys SPI_MOSI_DIR = 1 SPI_SS = 1 Где-то за 20 минут написал программу, заработала сразу, правда, тщательно не тестировал. Всё понравилось, кнопки читает, что ещё надо.
К сожалению, в МК нет хорошего интерфейса для таких задач, вот и «колхозят», кто как может.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения