Языки программирования - Генерация широтно-импульсной модуляции (ШИМ) микроконтроллером. - PRCY⮭net
Регулирование напряжения с помощью ШИМ. В примерах используется микроконтроллер PIC16F84.

Опять-таки сложных терминов я не знаю и употреблять не буду.

Если коротко, то ШИМ - это цифровой сигнал, с помощью которого можно задать аналоговый сигнал. На рисунке 1 приведена временная диаграмма ШИМ с постоянной скважностью.


Рисунок 1 - Временная диаграмма ШИМ

Период ШИМ T - величина постоянная (это время). На одном периоде укладывается один единичный импульс шириной T1 и один нулевой импульс шириной T0. При этом
T=T1+T2=const
Скважность ШИМ G - это и есть эквивалент амплитуды аналогового сигнала:
G=T1/T, 0 <= G <= 1
Изменением ширины единичного импульса можно регулировать средний уровень напряжения: если уровень логической единицы ШИМ Um= 5 вольт, то подав цифровой сигнал ШИМ на фильтр напряжения, на выходе фильтра можно получить аналоговое напряжение U с любой амплитудой в диапазоне от 0 до 5 вольт.

U=Um*G

В некоторых случаях применение фильтра необязательно - например, при регулировании яркости свечения светодиода, так как у него есть некоторая постоянная времени зажигания и гашения, и если период ШИМ меньше этой постоянной, то мерцания не будет. Но в некоторых случаях без фильтра не обойтись.

Естественно, чем меньше период ШИМ, тем "глаже" будет аналоговый сигнал, но уменьшение периода ведёт к тому, что увеличивается дискретность регулирования скважности (при генерации МК).

Пусть частота кварца, задающего тактовую частоту МК равна Fq. Один машинный такт в PIC выполняется за 4 периода частоты кварца. Таким время машинного такта.
Tmt = 4/Fq
В PIC16F84 имеется восьмиразрядный таймер TMR0, содержимое которого увеличивается на единицу за каждый машинный такт (при предделителе = 0). При переполнении TMR0 в регистре STATUS устанавливается бит T0IF. И если в бит разрешения прерывания T0IE имеет значение 1, то происходит вызов обработчика прерываний. Таймер сам "перезапускается" c нуля.

Сгенерировать ШИМ можно несколькими способами.

1) T1 и T0 отмеряются количеством выполненных команд. Этот способ самый грубый и неудобный. Да, он обеспечивает минимальный период ШИМ, но это бывает нужно очень редко. Кроме того, если кроме генерации ШИМ нужно производить другие вычисления (а их нужно!), то вообще дрова. Если ещё учесть, что команды ветвления выполняются за два такта, то для стабилизации периода ШИМ все ветви программы нужно "балансировать" (вставляя NOP), чтобы все ветви выполнялись за одинаковое время.

2) Определенный флаг Fs хранит текущее состояние ШИМ - 0 или 1. Включается прерывание по таймеру (в PIC16F84 таймер только один (восьмиразрядный) - TMR0) и по каждому прерыванию от таймера флаг инвертируется, и в зависимости от нового состояния флага в таймер загружается либо T0, либо T1. Таким образом, остальные вычисления выполняются независимо от генерации ШИМ, надо только следить за сохранением контекста (рабочего регистра, регистра статуса и т.д.) при входе в обработчик прерывания и восстановлением при выходе. сбрасывать флаг прерывания лучше сразу после перезагрузки TMR0 - если таймер сработает до выхода из обработчика, то сразу после выхода снова произойдёт прерывание - таким образом, ширина периода ШИМ останется постоянной. Конечно, если сделать предделитель = 0, а обработчик будет выполняться более чем за 256 команд, то ни о каком постоянном периоде и не надо мечтать. Этот способ позволяет задать T от 0 до 512 dt. (dt - условная единица времени), T1 и T0 от 0 до 256 dt с дискретностью регулирования 1 dt, где 1 dt практически равна Tmt (с очень небольшими погрешностями).

3) В этом способе 1 dt = 256*Tmt. Регистр таймера вообще не трогается - он крутится сам по себе, сам перезапускается, и точно раз в dt генерирует прерывание. Ниже приведена программа, которая использует именно этот способ. Обратите внимание, что аппаратный предделитель не используется, хотя ничто не мешает это сделать, если нужно подзамедлить слишком шустрый сей процесс. В комментариях всё описано.
���:
#include
#include<macroZC.inc>
__config b'11111111110010'
;------------------------------------------
;переменные
cblock 0x0c
;для сохранения контекста
rTempW
rTempZ
rTempSTATUS
;для шим
rShimFlag ;флаг тек.уровня шим
rShimTMR ;таймер шим
rShimT0 ;ширина нуля шим
rShimT1 ;ширина единицы шим
rShimLevelPrescaler ;счётчик - предделитель для уменьшения таймера
;ШИМ
rShimSkvazhPrescaler ;счётчик - предделитель для регулирования частоты
;вызова процедуры изменения скважности ШИМ
endc
;------------------------------------------
;константы
cShimLevelPrescaler equ .32 ;значение пред-ля rShimLevelPrescaler
cShimSkvazhPrescaler equ .115 ; значение пред-ля rShimSkvazhPrescaler
cShimT1max equ .10 ;макс ширина 0 шим
cShimT0max equ .10 ;макс ширина 1 шим
cShimTPeriod equ cShimT0max+cShimT1max ;период шим
;------------------------------------------
;макросы
;макрос перехода по условию Z=1
JZ macro lblJZ ;переход,если Z=1
btfsc STATUS,Z
goto lblJZ
endm
;макрос перехода по условию Z=0
JNZ macro lblJNZ ;переход,если Z=0
btfss STATUS,Z
goto lblJNZ
endm

;выбор состояния ключей по состоянию датчиков
;------------------------------------------
;вектор сброса
org 0
goto start
;------------------------------------------
;вектор прерывания
org 4
goto interrupt
;------------------------------------------
start ;инициализация спец регистров
bcf STATUS,RP1
bcf STATUS,RP0
bcf INTCON,GIE ;глоб запрет прерываний
bsf INTCON,T0IE ;разреш прерыв от таймера
bsf STATUS,RP0
movlw b'10011000'
movwf OPTION_REG
bcf STATUS,RP0
;------------------------------------------
;инициализация переменных
movlw cShimLevelPrescaler
movwf rShimLevelPrescaler

;запуск шим с минимального значения
movlw cShimTPeriod
movwf rShimT0
movwf rShimTMR
clrf rShimT1
clrf rShimFlag
clrf TMR0
;конец инициализации
bsf INTCON,GIE ;разреш прерываний
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; Основная программа - в ней используется флаг шим, о "существовании " прерывания
; она даже не "подозревает"
main
;...
;...
;...
;...
goto main
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;процедура изменения скважности ШИМ по определённому закону
; По сути, здесь задаётся мгновенное значение уровня аналогового напряжения.
; Процедура проверяет флаги, выставленные основной программой
; и корректирует скважность по нужному закону
ShimZakonT1T0

;Меняем T1 в пределах 0 ... cShimTPeriod
;...

;Вычисляем новое значение T0
SZ_Calc_T0
movlw -cShimTPeriod
addwf rShimT1,w
movwf rShimT0
comf rShimT0,f
incf rShimT0,f
return
;----------------------------------------------------------------------------
;Обработчик прерывания
interrupt
;сохранение контекста
movwf rTempW ;сохраняем W
JZ int_Z1 ;определяем и сохраняем Z
int_Z0 bcf rTempZ,0
goto $+2
int_Z1 bsf rTempZ,0
movf STATUS,w ;сохраняем STATUS
movwf rTempSTATUS
;-------------------

;уменьшение счётчика-предделителя смены флага шим
; и смены флага, если переполнение
int_ShimTMR
movf rShimTMR,w
JZ int_ChangeShim
decf rShimTMR,f ;уменьшение таймера шим
goto int_ShimEnd
;меняем флаг шим
int_ChangeShim
movf rShimFlag,w ;проверка текущего уровня шим
JNZ int_Shim1to0
int_Shim0to1;нужно сделать переход с 0 в 1
movf rShimT1,w ;загрузка таймера ШИМ значением T1
JZ int_Shim1to0 ;и если оно=0, сразу меняем флаг ШИМ опять
movwf rShimTMR
bsf rShimFlag,0
goto int_ShimEnd
int_Shim1to0;нужно сделать переход с 1 в 0
movf rShimT0,w ;загрузка таймера ШИМ значением T0
JZ int_Shim0to1 ;и если оно=0, сразу меняем флаг ШИМ опять
movwf rShimTMR
clrf rShimFlag
int_ShimEnd
;-------------------

; уменьшение счётчика-предделителя счётчик для смены скважности
; и вызов процедуры, если переполнение
int_ShimSkvazhPrescaler
decf rShimSkvazhPrescaler,f
JNZ int_ShimSkvazhPrescaler_End
movlw cShimSkvazhPrescaler
movwf rShimSkvazhPrescaler
call ShimZakonT1T0
int_ShimSkvazhPrescaler_End
;;;;;
;-------------------
ExitInt bcf INTCON,T0IF ;сброс флага прерывания по таймеру
;восстановление контекста
movf rTempSTATUS,w
movwf STATUS
movf rTempW,w
btfss rTempZ,0
goto exit_Z0
exit_Z1 bsf STATUS,Z
retfie
exit_Z0 bcf STATUS,Z
retfie ;возврат с разрешением прерываний
;------------------------------------------
end



Автор: Алексей1153
Information
  • Posted on 31.01.2010 20:55
  • Просмотры: 1901