Подзадача: как получить данные из этой Tiny13 "на стенде"? Можно, конечно, писать их в EEPROM, потом считать программатором. Это "вообще". Но в конкретном случае данные - это то, что поступает с АЦП (а нужно откалибровать этот АЦП, то есть высчитать множитель для данных с АЦП, потому что делитель входного напряжения различается от изделия к изделию), напряжение, которое меряет АЦП - это нестабильное сетевое. То есть, нужен именно реал-таймовый поток данных с АЦП, идущий одновременно с потоком данных с калиброванного вольтметра, замеряющего это же сетевое напряжение, чтобы пересчитывать два числа (данные с АЦП калибруемого прибора и данные с вольтметра).
Как получить поток данных с tiny13?
Я предположил, что это можно сделать софтверным uart-ом (к тому же, свободных ног на этой тини - всего одна, на остальных что-то висит: делитель АЦП, реле, светодиоды). Вроде бы предположение "имело право на жизнь": ресурсов процессора для скорости 9600 хватает.
Но столкнулся с тем, что расчетные частоты чуть-чуть не попадают в диапазон требуемых. То есть, данные идут, но только если скорость отличается от расчетной процента на два.
Сначала я предположил, что дело в неточности заводской калибровки: по документации она имеет точность 10%, а требуется 1%. Сделал процедуру автокалибровки по импульсам 100 гц другого процессора. Но в итоге пришел к тому же самому: скорость передачи данных должна быть выше процента на два, чем рассчитывалось. Может быть, я где-то ошибся в алгоритме (ниже)? Или сам заложенный принцип неверен - нельзя полагаться на такую реализацию межпроцессорного обмена, когда нет принципиальной стабильности внутреннего RC-генератора? (и надо делать, скажем, синхронный обмен. Но мне тут уже принципиально интересно стало "в чем же проблема").
Текст фрагмента программы:
Константы и определения:
.equ counter_prescaler=2 ; clk/8
.equ counter_preset=256-125 ; 9 600 000/(8*125) = 9 600
.equ max_bit = 10 ; start + 8bit + stop
.def accum = r16 ; аккумулятор (регистр общего пользования)
.def accum2 = r17 ; второй аккумулятор
.def accum3 = r18 ; ...
.def xl = r26
.def xh = r27
.def yl = r28
.def yh = r29
(инициализация таймера в основной программе - включение прерывания по переполнению и установка делителя. Установка ноги Tx в "1")
(фрагмент прерывания от переполнения таймера 0, передача очередного бита):
ldi accum, counter_preset ; 256-125
out timer0_data, accum ; загрузить регистр данных таймера 0
(...)
; accum = байт для передачи
; accum3 = номер передаваемого бита
cpi accum3, 0 ; start-bit ? (0)
breq tx_send_start
cpi accum3, max_bit-1 ; stop-bit ? (9)
breq tx_send_stop
; если не старт-бит и не стоп-бит - передавать биты с 1 по 8
lsr accum ; сдвинуть весь передаваемый байт через Carry flag
brcc tx_send_0 ; бит в Cf = 0 ? если да- то переход на "поставить 0"
; если нет - то "поставить 1"
tx_send_stop: ; стоп-бит (1)
nop ; nop-ы - для выравнивания тактовых длительностей удержания разных битов.
tx_send_1:
nop
tx_set_1 ; поставить 1 на ножке порта Tx (MOSI)
rjmp tx_send_continue
tx_send_start: ; поставить старт-бит (0)
nop
nop
nop
nop
tx_send_0:
tx_set_0 ; поставить 1 на ножке порта Tx (MOSI)
tx_send_continue: ; дальше - уже не принципиальные моменты, пересчет и сохранение переменных
inc accum3 ; увеличить номер бита для передачи
cpi accum3, max_bit ; сколько осталось бит для передачи (0..9)
breq tx_next ; если 10 -то поменять указатели и укоротить очередь
st X, accum ; сохранить сдвинутый байт ==>>
rjmp tx_bit_pointer_store ; выход с сохранением нового количества бит
tx_next:
clr accum3 ; подготовить передачу следующего байта (0й)
ldd accum2, Y+1 ; загрузить указатель байта
inc accum2 ; подвинуть указатель байта на следующую позицию
ld accum, Y ; загрузить длину очереди
dec accum ; уменьшить длину на 1
st Y, accum ; и сохранить длину
tst accum ; проверить: длина очереди =0?
brne tx_continue
clr accum2 ; если да - то обнулить указатель байта
tx_continue:
std Y+1, accum2 ; сохранить указатель
tx_bit_pointer_store:
std Y+2, accum3 ; сохранить номер передаваемого бита
Вот в таком виде оно все работает.
НО!
если задать counter_preset=256-125-1 - то уже нет.
А если counter_preset=256-125+5 - то еще да. Почему получается "середина" (от +/- 1%) не там? Я что-то не учитываю в алгоритме? Или ошибочная реализация?
Спасибо за внимание
