PICのPWMの正しい使い方について教えてください
PIC12F1822用のPWMの基本動作テストプログラムを作りましたが、PWM出力が意図どおりに得られません。
(XC8のテストプログラムを下に添付。テストパターン1/2の切り替えはコメントアウトをお願いします。
アナライザの出力を画像で添付しました。)
テスト目的: タイマー0の周期割り込みに合わせて、PWMのデューティー比(または周期)を変更するプログラム
テストパターン 1 : パルスのデューティー比を変えるテスト
質問1: パルスのデューティー比を変えるテストは、PWM設定の一回おきにCCPからのPWMの出力が出ません
テストパターン 2 : パルスの周波数を変えるテスト
質問2: 周波数を変えるテストはOKに見えるものの、1回目のPWMがHighとなるのと、割り込みの11回目、13回目あたりでPWM出力が欠落する
【回答のお願い】
どうも、PWMのレジスタ設定に手順、またはタイミングがあるのではないかと思いますが、原因がわからず困っております。
PICに詳しい方からのご指導をよろしくお願いいたします。
K.A.
------------------------------------------------------
/*
* File: PIC12F1822 PWC
* Author: K.A.
*
* Created on 2014/07/20
*
* タイマー0の周期割り込みに合わせて、PWMのデューティー比(または周期)を変更するプログラム
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=ON, BOREN=ON, MCLRE=OFF
#define _XTAL_FREQ 8000000 // クロック8MHz
/*
*
*/
// タイマー割込みの処理
int TMR0_Count = 0; // タイマーの割込み発生回数をカウントする変数
void interrupt Timer0(void) {
// タイマー0の割込み発生か? Timer0 は 8bitの オーバーフロー・カウンタ
if (TMR0IF == 1) {
TMR0_Count++;
if (TMR0_Count > 5) {
TMR0_Count = 0;
if (RA5 == 0) RA5 = 1; else RA5 = 0; // RA5は、動作表示用LED
}
TMR0IF = 0; // タイマー0割込フラグをリセット
}
}
int main(int argc, char** argv) {
unsigned int pulse_width = 5;
OSCCON = 0b01110010; // 内部クロックは8MHz
ANSELA = 0b00000000; // すべてをデジタルI/Oに割当
TRISA = 0b00001000; // すべてのピンは出力に割当てる(RA3は入力専用)
PORTA = 0b00000000; // 出力ピンの初期化(全て'0'にする)
RA5 = 0; // 動作確認用LED
// Timer0 Timer0 は 8bitの オーバーフロー・カウンタ
OPTION_REG = 0b00000001; // 内部クロックでTIMER0を使用、プリスケーラカウント値 1:2
// bit5:0=Fosc/4, bit3:0=PreScaler_ON, bit2-0:PreScaler
TMR0 = 0; // タイマー0の初期化 (+2cycle)
TMR0IF = 0; // タイマー0割込フラグを0にする
TMR0_Count = 0; // 割込み発生の回数カウンターを0にする
TMR0IE = 1; // タイマー0割込みを許可する
GIE = 1; // 1: 全割込み処理を許可する
/* PWM */
TRISA2 = 1; // RA2 出力をサスペンド
TMR2IF = 0; // TMR2 フラグをクリア
CCP1SEL = 0b0; // CCP1/P1Aの機能をRA2に割り当てる
CCP1CON = 0x0C; // PWM モード
// Period を 設定する xx ms=((PR2)+1)*4*125ns(8MHz)* PreScaler(x1-x64)
PR2 = 127; // 4.096ms (プリスケーラが x64 の場合)
T2CKPS0 = 0b1; // プリスケーラ 00:x1, 01:x4, 10:x16, 11:x64
T2CKPS1 = 0b1; //
CCPR1L = pulse_width >> 2; // パルス幅上位8bit
CCP1CON = ((pulse_width & 0x0003) << 4) | 0x0C; // パルは幅下位2bit
TMR2ON = 1; // TMR2 カウント開始
while (TMR2IF == 0) { /** / do nothing /**/ }
TRISA2 = 0; // RA2に出力を接続
while (1) {
if (TMR0_Count == 0) {
/**** テストパターン 1 : パルスのデューティー比を変えるテスト ****/
// パルスのデューティー比を変えるテストは、PWM設定の一回おきにCCPがPWMの出力が出ません。 なぜ?
pulse_width = pulse_width + 10;
if (pulse_width > 500) pulse_width = 5;
/**/
/**** テストパターン 2 : パルスの周波数を変えるテスト **** /
// 周波数を変えるテストはOKに見えるが、
// 1回目のPWMがHighとなるのと、割り込みの11回目、13回目あたりでPWM出力が欠落する
PR2 = PR2 - 8 ;
if (PR2 < 8) PR2 = 127;
pulse_width = PR2 ; // Duty 25% に相当
/**/
CCPR1L = pulse_width >> 2;
CCP1CON = ((pulse_width & 0x0003) << 4) | 0x0C;
TMR0_Count++; // 続けて PWMの設定変更をしないためのフラグ代わり
}
}
return (EXIT_SUCCESS);
}
補足
回答ありがとございます。 要求精度としては、1クロック(12.5usec)の誤差を目標としています。 対象の80KHzは、センサーからの出力で、常に安定しているわけではないようです。 検討段階では、立上り間隔でタイマー1のレジスタを取得し、 前回との差分を、5msec間取得し、平均をとってクロック数を算出しようと思っていました。 周波数の計算をPICで行うと処理時間が間に合わないことが分かりましたので、外部で行うことにしました。 実験レベルですので、詳細に検討していきます。