Помогите допилить проект на Attiny13

Tester500

Хочу сделать замену для стандартной платы (на RX2B ) китайских игрулек, чтоб можно было игрульку подключить к приемнику PPM путевой аппаратуры. ТЗ два входных канала PWM (лево-право, вперед-назад) из приемника на входе, четыре выхода с PWM для управления двумя AC моторами. Схема управления моторами от RX2B схемы (кроме сигнала турбо). Сигнал на входе: длительность от 1мс до 2мс, центр 1,5мс. Сигналы относительно друг друга могут появиться в любое время. Я наваял код на ассемблере, в нем программный деPWM на два канала, и програмный PWM на четыре ноги. дорихтовать код ктонибудь поможет?

Tester500

Кол-во просмотров растет, комментариев ноль. Ну да ладно. Реализация будет такая:
Attiny13 -8 ног. Мне нужно две ноги для входа (энкодер PWM), две для управления одним движком, две для управления другим (PWM выход). Чтобы 6 ног было свободными нужно использовать внутренний RC генератор. Тогда макс. частота 9.6 Мгц. Внутри один таймер, значит энкодер (на входе) и PWM (на выходе) будет программным. На входе сигнал с макс. длительностью 2 мс, полезная длительность (несущая нужную информацию) 1мс. 1мс это килогерц. если я хочу получить хотя бы 100 выборок за 1мс (45 для влево, 45 для вправо и 10 середина), мне нужно иметь 100кГц. Что такое 100кГц для 9.6Мгц? Это всего 96 тактов процессора, т.е. 96 команд. Поэтому чистый ассемблер. Настраваю таймер на 100кГц. Вся программа в прерывании. Написан кусок кода с оцифровкой кода и PWM_out. Самый долгий проход 34 такта. Остается добавить логику коммутации выходов. PWM_out загружает значение для сравнения при достижении счетчика PWM FF, при загрузке нужно выставить нужные ноги и загрузить нужное число в регистры сравнения. Писал на коленке, комментарии убогие, за чистоту кода звиняйте - “как шмогла”
PS: у меня ATtiny13A, у нее можно аппаратно отключить модуль ADC.

Tigra74
Tester500:

Кол-во просмотров растет, комментариев ноль.

Так ни кода,ни схемы.

Если я правильно понял-Вы хотите сделать декодер ППМ с выходом сразу на ключи моторов,и чтоб попорцианально менялась скорость моторов?
Причём всё на одной микросхеме МК?

Навроде этого?

Tester500

Сегодня вечером отправлю и схему, и код.

Tester500

Что я хочу заменить:

из этой схемы выкидываем радиочасть, RX2B, и вставляем тиньку. PB0,PB1 вход с приемника; PB2,PB3 выход PWM для 1 мотора; PB4,PB5 выход PWM для 2 мотора. Приемник:

На данный момент код такой:

/*
* rx2b_asm.asm
*
* Created: 21.10.2013 19:40:25
* Author: tester500
*/

/*
* CPU use internal RC clock 9,6 MHz
*/

.def temp=r16
.def depwm_a=r17
.def depwm_b=r18
.def pwm_t=r19
.def cr_a=r20
.def cr_b=r21
.def pin_i=r22
.def ch_a_in=r23
.def ch_b_in=r24

.org 0x0000

;Interrupt Vectors
rjmp RESET ;RESET
reti ;INT0
reti ;PCINT0
reti ;TIM0_OVF
reti ;EE_RDY
reti ;ANA_COMP
rjmp Timer_INT ;TIM0_COMPA
reti ;TIM0_COMPB
reti ;WDT
reti ;ADC

;INT0:
;PCINT0:
;TIM0_OVF:
;EE_RDY:
;ANA_COMP:
;TIM0_COMPA:
;TIM0_COMPB:
;WDT:
;ADC:

RESET:
;init procedure
ldi temp,RAMEND ;Set Stack Pointer to RAMEND 0x9f
out SPL,temp

;shutdown adc
clr temp ;0x0 -> ADCSRA
out ADCSRA,temp
inc temp ;0x1 -> PRR
out PRR,temp

;PORTB variable initializations
;PORTB0 for 1: __PB0 as Input - Reserved for pin PB0
;PORTB1 for 1: __PB1 as Input - Reserved for pin PB1
;PORTB2 for 1: __PB2 as Output - Reserved for pin PB2
;PORTB3 for 1: __PB3 as Output - Reserved for pin PB3
;PORTB4 for 1: __PB4 as Output - Reserved for pin PB4
ldi temp,0x1C
out PORTB,temp
out DDRB,temp

clr depwm_a ; temp timer chanel A
clr depwm_b ; temp timer chanel B
clr cr_a ; Compare Reg for Chanel A
clr cr_b ; Compare Reg for Chanel B
clr ch_a_in ; Ch_A
clr ch_b_in ; Ch_B
clr pwm_t; software PWM counter

;TIMER_COUNTER_0 - -
;
;Waveform Generation Mode = Clear Timer on Compare Match TOP=OCRA
;Clock Select = clk (No prescaling)
;Timer Overflow Interrupt Enable = Enabled
;Output Compare Register A = 0xBF
;Counter Frequency = 50,0 kHz
;Counter Period = 20,0 usec
;Seconds per Count = 104,167 nsec
;Counts per Second = 9,6 MHz
;Output Compare Register A Time = 19,896 usec
ldi temp,0x04 ;(1<<OCIE0A)
out TIMSK0,temp
ldi temp,0xBF ;OCR0A = 0xBF
out OCR0A,temp
ldi temp,0x02 ;(1<<WGM01)|(0<<WGM00)
out TCCR0A,temp
ldi temp,0x01 ;(0<<WGM02)|(0<<CS02)|(0<<CS01)|(1<<CS00)
out TCCR0B,temp
sei

main_loop:
nop
rjmp main_loop

;Timer0 Overflow procedure
Timer_INT:
in pin_i,PINB ;store PinB to R16

;PWM procedure
start_pwm:
inc pwm_t

cp pwm_t,cr_a
brne pwm_b_start
tst cr_a ; if cr_a eq (0x00 or 0xFF) goto test cr_b
breq pwm_b_start
cpi cr_a,0xFF
breq pwm_b_start
nop ; pwm_t eq cr_a, need down PIN out chanel

pwm_b_start:
cp pwm_t,cr_b
brne pwm_ff
tst cr_a ; if cr_b eq (0x00 or 0xFF) goto pwm_ff
breq pwm_ff
cpi cr_a,0xFF
breq pwm_ff
nop ; pwm_t eq cr_b, need down PIN out chanel

pwm_ff:
cpi pwm_t,0xFF
brne pwm_end
nop ; pwm_t=ff; load cr_a, cr_b; need set out_PIN if cr_a or cr_b eq (0x00 or 0xFF)

pwm_end:

bst pin_i,0 ; Test Chanel A
brts Pin0_not_null ; jump if Pin0 =1
tst depwm_a
brne tim_cha_notnull ; jump if Pin_chA = 0 Timer_chA > 0

Test_chB:
bst pin_i,1 ; Test Chanel B
brts Pin1_not_null ; jump if Pin1 =1
tst depwm_b
brne tim_chb_notnull ; jump if Timer Chanel B > 0
rjmp ret_int

tim_cha_notnull:
mov ch_a_in,depwm_a
clr depwm_a
rjmp Test_chB

tim_chb_notnull:
mov ch_b_in,depwm_b
clr depwm_b
rjmp ret_int

Pin0_not_null: ; inc Timer Chanel A
cpi depwm_a,0xFF
breq Test_chB
inc depwm_a
rjmp Test_chB

Pin1_not_null:
cpi depwm_b,0xFF
breq ret_int
inc depwm_b
rjmp ret_int

ret_int:
nop ; BreakPoint for control ch_a_in,ch_b_in
reti

Основной код выполняется в прерывании начиная с “Timer_INT:”, перед reti точка останова для контроля, если в симуляторе дергать PIN0 PIN1 руками, имитируя входной сигнал, то ch_a_in ch_b_in будут содержать длительность сигнала на входе.
Таймер настроен от балды, главное вылизать код в прерывании, чтобы он не вылез за 96 команд. Настройка портов - PB0,PB1 вход; PB2,PB3,PB4 выход; PB5 пока код не отладим не трогаю - ресет нужен для программирования.
Логика такая:
На входе сигнал длительностью от 1мс до 2мс, меряем его весь (100 едениц на 1мс), если сигнал на входе верный, то он не должен быть больше 200 (0xC8), если его длительность больше 255(0xFF), то обрезаем до 255 (0xFF). Результат идет в ch_a_in ch_b_in. Это уже сделано и в симуляторе работает.
PWM для выхода тоже работает, если в симуляторе в cr_a поместить длительность импульса (0<cr_a<0xFF), то программа будет попадать в точку “nop ; pwm_t eq cr_a, need down PIN out chanel”. С cr_b аналогично “nop ; pwm_t eq cr_b, need down PIN out chanel”. В этих точках нужно опускать выходной пин, логику выбора пина надо доделать. Когда счетчик PWM выхода (pwm_t) дойдет до 0xFF, нужно из ch_a_in, ch_b_in грузить значения в cr_a,cr_b, вот тут надо правильно вычислить что было на входе (посчитать длительность) и установить нужный выходной пин.

Dinotron

Ну уважаю я строителей велосипедов! Жду затаив дыхание результата. Сильно человек делает. Ассемблером мать! Удачи.

Иван
  • можно проще - на свои выходы приёмыша навесить потроха от серв например 9ти грамовых, вместо переменника подстроечник- выствить нейтраль и ВСЁ - у вас два рега для движков.

с тинькой смысла морочиться тут никакого, ИМХО 😃)

Tester500
Иван:
  • можно проще - на свои выходы приёмыша навесить потроха от серв например 9ти грамовых, вместо переменника подстроечник- выствить нейтраль и ВСЁ - у вас два рега для движков.

с тинькой смысла морочиться тут никакого, ИМХО 😃)

Одна игрулька - две распотрашенных сервы, а десять игрулек?
Я не ищу решение для одной вещи, у меня их много. У нас неисправные (в основном электроника) игрушки продают на вес. Чтобы сделать то что стоит в серве, мне не надо ее потрошить, это я и сам могу быстро сделать, там ниче трудного нет.

Иван

а код ваш в тиньку загоняли - нормально рулит?

Dinotron

Надо же работает. Криво правда, ну у меня суровее. 32 мега с Jtag-ом. Забавно. 😃
P. S. Ну порты поменять. Вооружение вы всё-таки используете. Ну а встроенный РС блин всё портит, но работает.

Tester500

до заливки в тело еще не дошло, сегодня буду доделывать. Код получается небольшой, я решил увеличить кол-во выборок до 128 на 1мс (128кГц), изменение в коде OCR0A = 0x4A

Dinotron

Движки пищат противно. Изменение окра не сильно помогло. Но всё-таки! 😃

Tester500

Какая частота PWM лучше для коллекторных движков - 2кГц, 1кГц, 500Гц?

dimaris

Оптимальная частота 3кГц, меньше влияние помех от двигателей на аппаратуру и сервоприводы…

Tester500

У меня при 2кГц получается 64 шага, если увеличу до 4кГц, то - во первых будет всего 32 шага, во вторых надо будет массштабировать, код может вылезти за прерывание. Оставлю 2кГц, авось прокатит. Сегодня буду катать в железе.

2 months later
Tester500

Полученный код ниже. Чуть позже опишу FUSE, надо ногу RESET отдать под IO порт, разрешить Internal OSC 9.6mHz, еще можно разрешить WDT 1s
В приведенном коде порт PB5 соединен с RESET для отладки и прошивки, хотя функционал его работы полностью написан.

/*
* rx2b_asm.asm
*
* Created: 21.10.2013 19:40:25
* Author: tester500
*/

/*
* CPU use internal RC clock 9,6 MHz
*/

.def temp=r16
.def depwm_a=r17
.def depwm_b=r18
.def pwm_t=r19
.def cr_a=r20
.def cr_b=r21
.def pin_i=r22
.def ch_a_in=r23
.def ch_b_in=r24
.equ cha_p_left=2 ; PB2
.equ cha_p_right=3 ; PB3
.equ chb_p_left=4 ; PB4
.equ chb_p_right=5 ; PB5

.org 0x0000

;Interrupt Vectors
rjmp RESET ;RESET
reti ;INT0
reti ;PCINT0
reti ;TIM0_OVF
reti ;EE_RDY
reti ;ANA_COMP
rjmp Timer_INT ;TIM0_COMPA
reti ;TIM0_COMPB
reti ;WDT
reti ;ADC

;INT0:
;PCINT0:
;TIM0_OVF:
;EE_RDY:
;ANA_COMP:
;TIM0_COMPA:
;TIM0_COMPB:
;WDT:
;ADC:

RESET:
;init procedure
ldi temp,RAMEND ;Set Stack Pointer to RAMEND 0x9f
out SPL,temp

;shutdown adc
clr temp ;0x0 -> ADCSRA
out ADCSRA,temp
inc temp ;0x1 -> PRR
out PRR,temp

;PORTB variable initializations
;PORTB0 for 1: __PB0 as Input - Reserved for pin PB0
;PORTB1 for 1: __PB1 as Input - Reserved for pin PB1
;PORTB2 for 1: __PB2 as Output - Reserved for pin PB2
;PORTB3 for 1: __PB3 as Output - Reserved for pin PB3
;PORTB4 for 1: __PB4 as Output - Reserved for pin PB4
;PORTB5 for 1: __PB5 as Output - Reserved for pin PB5

;PORTB5 NOT Connected to IO PORT! Connected to reset
;for debug and ISP. On code functions for PB5 full
;present

ldi temp,0x1C ; for full need 0x3C (PB5 - out)
out PORTB,temp
out DDRB,temp

clr temp
clr depwm_a ; temp timer chanel A
clr depwm_b ;temp timer chanel A
clr pwm_t ; software PWM
clr cr_a
clr cr_b
clr pin_i
clr ch_a_in
clr ch_b_in

;TIMER_COUNTER_0 - -
;
;Waveform Generation Mode = Clear Timer on Compare Match TOP=OCRA
;Clock Select = clk (No prescaling)
;Output Compare Register A = 0x4A
;CPU clock = 9,6 MHz
ldi temp,0x04 ;(1<<OCIE0A)
out TIMSK0,temp
ldi temp,0x4A ;OCR0A = 0x4A 127kHz
out OCR0A,temp
ldi temp,0x02 ;(1<<WGM01)|(0<<WGM00)
out TCCR0A,temp
ldi temp,0x01 ;(0<<WGM02)|(0<<CS02)|(0<<CS01)|(1<<CS00)
out TCCR0B,temp
sei

main_loop:
nop
rjmp main_loop

;Timer0 Overflow procedure
Timer_INT:
in pin_i,PINB ;store PinB to R16

;PWM procedure
start_pwm:
inc pwm_t

cp pwm_t,cr_a
brne pwm_b_start
tst cr_a ; if cr_a eq (0x00 or 0xFF) goto test cr_b
breq pwm_b_start
cpi cr_a,0x40
breq pwm_b_start
cbi PORTB,cha_p_left ; pwm_t eq cr_a, need clear PIN out chanel
cbi PORTB,cha_p_right
pwm_b_start:
cp pwm_t,cr_b
brne pwm_ff
tst cr_a ; if cr_b eq (0x00 or 0xFF) goto pwm_ff
breq pwm_ff
cpi cr_a,0x40
breq pwm_ff
cbi PORTB,chb_p_left ; pwm_t eq cr_b, need clear PIN out chanel
cbi PORTB,chb_p_right
pwm_ff:
cpi pwm_t,0x40
brne pwm_end
; pwm_t=ff; load cr_a, cr_b; need set out_PIN if cr_a or cr_b eq (0x00 or 0xFF)
clr pwm_t
;cr_a chanel
cpi ch_a_in,0x83
brlo cr_a_full_left
cpi ch_a_in,0xBD
brlo cr_a_left
cpi ch_a_in,0xC5
brlo cr_a_stop
cpi ch_a_in,0xFE
brlo cr_a_right
cr_a_full_right:
; cr_a full right
ser cr_a
cbi PORTB,cha_p_left ; clear pin_left, set pin_right
sbi PORTB,cha_p_right
rjmp cr_b_pwm_set
cr_a_full_left:
; cr_a full left
ser cr_a
sbi PORTB,cha_p_left ; set pin_left, clear pin_right
cbi PORTB,chb_p_right
rjmp cr_b_pwm_set
cr_a_stop:
; stop cr_a
clr cr_a
cbi PORTB,cha_p_left ; clear pin_left,pin_right
cbi PORTB,chb_p_right
rjmp cr_b_pwm_set
cr_a_left:
; cr_a left
ldi cr_a, 0xC0
sub cr_a,ch_a_in
sbi PORTB,cha_p_left ; set pin_left, clear pin_right
cbi PORTB,cha_p_right
rjmp cr_b_pwm_set
cr_a_right:
; cr_a right
mov cr_a,ch_a_in
subi cr_a,0xC0
cbi PORTB,cha_p_left ; clear pin_left, set pin_right
sbi PORTB,cha_p_right
cr_b_pwm_set:
;cr_b chanel
cpi ch_b_in,0x83
brlo cr_b_full_left
cpi ch_b_in,0xBD
brlo cr_b_left
cpi ch_b_in,0xC5
brlo cr_b_stop
cpi ch_b_in,0xFE
brlo cr_b_right
cr_b_full_right:
; cr_b full right
ser cr_b
cbi PORTB,chb_p_left ; clear pin_left, set pin_right
sbi PORTB,chb_p_right
rjmp pwm_end
cr_b_full_left:
; cr_b full left
ser cr_b
sbi PORTB,chb_p_left ; set pin_left, clear pin_right
cbi PORTB,chb_p_right
rjmp pwm_end
cr_b_stop:
; stop cr_b
clr cr_b
cbi PORTB,chb_p_left ; clear pin_left,pin_right
cbi PORTB,chb_p_right
rjmp pwm_end
cr_b_left:
; cr_b left
ldi cr_b, 0xC0
sub cr_b,ch_b_in
sbi PORTB,chb_p_left ; set pin_left, clear pin_right
cbi PORTB,chb_p_right
rjmp pwm_end
cr_b_right:
; cr_b right
mov cr_b,ch_b_in
subi cr_b,0xC0
cbi PORTB,chb_p_left ; clear pin_left, set pin_right
sbi PORTB,chb_p_right
pwm_end:

bst pin_i,0 ; Test Chanel A
brts Pin0_not_null ; jump if Pin0 =1
tst depwm_a
brne tim_cha_notnull ; jump if Pin_chA = 0 Timer_chA > 0

Test_chB:
bst pin_i,1 ; Test Chanel B
brts Pin1_not_null ; jump if Pin1 =1
tst depwm_b
brne tim_chb_notnull ; jump if Timer Chanel B > 0
rjmp ret_int

tim_cha_notnull:
mov ch_a_in,depwm_a
clr depwm_a
rjmp Test_chB

tim_chb_notnull:
mov ch_b_in,depwm_b
clr depwm_b
rjmp ret_int

Pin0_not_null: ; inc Timer Chanel A
cpi depwm_a,0xFF
breq Test_chB
inc depwm_a
rjmp Test_chB

Pin1_not_null:
cpi depwm_b,0xFF
breq ret_int
inc depwm_b
rjmp ret_int

ret_int:
wdr
reti

TheTERMINATOR

Респект. Для лучшей повторяемости, предлагаю вложить скрин с фьюзами и прошивку.

У меня как раз есть кошки, для тренировки…

2 months later
Tester500

Чета оно мне не очень понравилось, в логике не хватает красоты и простоты. Делаю версию 2.
План:
Тактовая частота - внутренний источник 9.6 мГц
Таймер - в режиме “Fast PWM TOP=0xFF”; прерывание по переполнению для оцифровки двух входных каналов; прерывания OCRA OCRB для формирования управляющего сигнала на выходе.
Таймер настроен на частоту 37,5 кГц. Для каждого направления в канале использую 36 шагов. Для PWM на выходе эти 36 шагов буду массштабировать (умножать) на 7. Почему 36? Потому что 36*7=252, а таймер 8-битный, макс значение регистра сравнения 255. Выбрал что поближе.
На выходе буду использовать (в схематике) L9110, ибо оно меньше бакса на мотор получается, да и места берет меньше.

mix_mix

Добрый день пару дней назад меня тоже заинтересовала эта тема, я хочу сделать управление до шести составов на железной дороге.через мощный полевик планирую выдавать на рельса сигнал РРМ. в каждом локомотиве стоит приемник. Через колеса, диодный мост спрямляет переменный сигнал PPM и этим напряжением питает двигатели откликаясь на свой канал, у игроков пульт с одним резистором. Сейчас собираю материал, ваша статья самая интересная для меня но хотел бы вам показать еще эту статью nick_shl.at.tut.by/Work/Receiver.htm, В ней понравилась идея определения полярности сигнала и некоторый другие вещи инициализации сейчас не знаю правда на сколько это актуально потому как макет еще не делал но по описанию разумно. Программу постараюсь писать сам, не подглядывая к вам может потом найдем оптимальные коды и объединим результат. Давно ничего не писал правда, но дети хотят интересные игрушки буду вспоминать, начну в понедельник на работе дома не хватает времени. еще хочу учесть функцию тормоза при малых длительностях команды в пределах 1- 1,2мс, шунтировать двигатель через полевик.

Tester500

По ссылке проект, в котором на входе PPM, на выходе несколько PWM каналов. У меня это часть в приемнике. Я делаю схемку, которая принимает два PWM канала, на которых обычно висят сервы, и управляю двумя коллекторными движками. Подобную схему я не видел в инете, либо один контроллер на один движок. Хотя даже в одной тиньке сил хватает на два движка. Основная проблема с входным сигналом, я не знаю как приходят PWM импульсы, предполагаю что сигналы друг с другом вообще никак не связанны (может там два разных приемника).
Насчет питания - можно проще сделать - на рельсы подавать постоянный ток, и туда-же через конденсатор подмешивать полезный сигнал, на приемной стороне через такой-же конденсатор снимать сигнал.

mix_mix

Как я вижу вашу задачу, на входе по таймеру записываю число первого времени в один регистр, затем число второго времени в другой регистр. затем обсчет величин. Я предлагаю дискретность сделать равной десяти. и вычитать из числа эквивалента 1 мс наше подсчитанное число если флаг 0 или или отрицания то переход к заданию нуля на выходе шим, если флага нет, то вычитаем из 1,1мс, и т.д. до 2мс, при этом в цикле вычитания можно присваивать множитель переменной для последующего умножения шим или сразу эквивалент скважности. затем также второй канал. у вас на всю математику остается 16мс. можно уложиться с головой.

mix_mix

Насчет питания постоянным током и подмешиванием через конденсатор сигнала, насколько я понимаю такая схема будет работать если амплитуда сигнала будет выше постоянной составляющей, иначе грубо говоря нужно будет просаживать постоянку на землю. а тут диодный мост, хотя может даже и одного диода хватит (отрицательной составляющей же нет как в синусоидальном сигнале), и вот уже постоянка. итого до диода сигнал после питание! лично мне нравиться, хотя готов рассмотреть и другие варианты.

Tester500
mix_mix:

Как я вижу вашу задачу, на входе по таймеру записываю число первого времени в один регистр, затем число второго времени в другой регистр. затем обсчет величин.

Допустим есть 8-битный регистр-счетчик времени и счет идет вверх по кругу (по достижению значения 255 он обнуляется и считает снова). Предположим у нас 10 шагов счетчика на максимальный сигнал на входе. В первый регистр заносим 254 (именно в этот момент сигнал на входе скакнул вверх), во второй регистр заносим 5. Как будет выглядеть обсчет величин? А если сигнал на входе отвис на два-три и т.д. периода регистра-счетчика?
Обычно делается не так. Берем регистр, при появлении сигнала на входе двигаем этот регистр вверх с проверкой граничных величин. Как тока сигнал на входе изменился, копируем полученное число в регистр переменную длительности сигнала на входе. Основной фокус проверка тех самых граничных величин.
Теперь о проблемах. В Attiny13 один 8-битный таймер и внутренний генератор частоты на 9.6 мГц. Для того чтобы получить на выходе не просто сигнал направления (для двигателя вперед-назад), а чтобы этот сигнал был ШИМом для движка, есть два способа - аппаратный ШИМ, и программный ШИМ. Программный шим я уже сделал, но у него очень низкая частота. Для увеличения частоты нужен аппартный ШИМ. Теперь математика - используем режим “Fast PWM TOP=0xFF” для таймера. У таймера три прерывания - два прерывания для ШИМ, и одно по переполнению. Прерывание по переполнению используем для обсчета входного сигнала. 9600000/256=37500. На входе сигнал 1 мсек =1кГц (вперед или назад) у нас 37,5 периодов таймера максимальное разрешение таймера. буду считать 36 влево или вправо, а 1 на границу нуля. Выходной сигнал от 0 до 255. 255/36 = семь с копейками. Значит полученное значение умножаем на 7 и в регистр сравнения таймера для формирования ШИМ. Остается снабдить эту конструкцию проверкой границ и определения рабочего выходного канала (вперед или назад).

Dinotron

Ну творчески вы подошли к решению. На тиньке! Я вот дармоед. Скопировал дурину и привинтил к китайским обломкам. Да не изящно, но все библиотеки в моём распоряжении и можно не заморачиваться с программно/аппаратной реализацией ШИМа. 😃 А вам, как говорится, респект и уважуха!