« 【PICマイコン】PIC12F1572で16bitPWM | トップページ | 【16番】RCサーボモーターでKATO HOユニトラック手動ポイントの電動化 »

2018/05/20

【PICマイコン】PIC12F1572で16bitPWM(サンプルコード)

きれいに貼り付きませんが、試しにプログラムを掲載してみます。動作の保証をするものではないので、自己責任でお使い下さい。

(参考)
【PICマイコン】PIC12F1572で16bitPWM



//--------------------------------------------------------------------------
// Include files
//--------------------------------------------------------------------------
#include <xc.h>
#include <stdio.h>
#include <stdint.h>

//--------------------------------------------------------------------------
// Fosc & XTAL_FREQ
//--------------------------------------------------------------------------
#define _FREQ 4 // Fosc = _FREQ MHz
#define _XTAL_FREQ (_FREQ*1000000)

//--------------------------------------------------------------------------
// PIC12F1572 Configuration Words
//--------------------------------------------------------------------------
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection
#pragma config WDTE = OFF // Watchdog Timer
#pragma config PWRTE = OFF // Power-up Timer
#pragma config MCLRE = OFF // MCLR Pin Function Select
#pragma config CP = OFF // Flash Program Memory Code Protection
#pragma config BOREN = ON // Brown-out Reset Enable
#pragma config CLKOUTEN = OFF // Clock Out Enable

// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection
#if _FREQ == 32 // Fosc = 32MHz
#pragma config PLLEN = ON // 4x PLL ON
#else // Fosc != 32MHz
#pragma config PLLEN = OFF // 4x PLL OFF
#endif
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset Enable
#pragma config BORV = LO // Brown-out Reset Voltage Selection
#pragma config LPBOREN = OFF // Low Power Brown-out Reset Enable
#pragma config LVP = OFF // Low-Voltage Programming Enable

// OSCCON
#if _FREQ == 1 // Fosc = 1MHz
#define INIT_OSCCON 0b01011000
#elif _FREQ == 2 // Fosc = 2MHz
#define INIT_OSCCON 0b01100000
#elif _FREQ == 4 // Fosc = 4MHz
#define INIT_OSCCON 0b01101000
#elif _FREQ == 8 // Fosc = 8MHz
#define INIT_OSCCON 0b01110000
#elif _FREQ == 16 // Fosc = 16MHz
#define INIT_OSCCON 0b01111000
#elif _FREQ == 32 // Fosc = 32MHz (4x PLL ON)
#define INIT_OSCCON 0b01110000
#else
#error _FREQ is not defined properly.
#endif

// End of system.c

//--------------------------------------------------------------------------
// PICマイコン設定
//--------------------------------------------------------------------------
/* 【PIC12F1572】
* +-----+
* +5V VDD|1 8|VSS GND
* RA5|2 7|RA0 PWM2 | +5V | GND
* AN3 RA4|3 6|RA1 PWM1 | +5V | GND
* nSWITCH+WPU RA3|4 5|RA2 AN2
* +-----+
//--------------------------------------------------------------------------*/
#define PWM2 RA0
#define PWM1 RA1
#define AN2 PA2
#define nSWITCH RA3
#define AN3 RA4

void SYSTEM_Initialize(void)
{
// OSCILLATOR_Initialize();
OSCCON = INIT_OSCCON; // INTOSCの発振周波数を設定
OPTION_REGbits.nWPUEN = 0; // 入力ピンのプルアップ無効化を解除

// I/O pin
TRISA = 0b00011100; // RA2,RA3,RA4は入力モード
WPUA = 0b00001000; // RA3はWeak Pull-up
ODCONA = 0b00000000;
PORTA = 0b00000000; // 出力ピンの初期化(全てLOWにする)
ANSELA = 0b00010100; // RA2,RA4はアナログモード

}

//--------------------------------------------------------------------------
// 定数・グローバル変数
//--------------------------------------------------------------------------
// PWMデューティ
#define DUTY_MIN 1000 // uS
#define DUTY_MAX 2000 // uS

//--------------------------------------------------------------------------
// 16-bit PWM Module
//--------------------------------------------------------------------------
void PWM_Reload(uint8_t PWM_CH)
{
switch(PWM_CH){
case 1:
// Reload Operationの設定
PWM1LDCON = (1 << 7) // bit7 LDA=1: Loads OF/PH/DC/PR buffers on period ends
| (0 << 6) // bit6 LDT=0: Periodical Loads OF/PH/DC/PR buffers
| (0 << 0); // bit1-0 LDS=0: Reserved
break;
case 2:
// Reload Operationの設定
PWM2LDCON = (1 << 7) // bit7 LDA=1: Loads OF/PH/DC/PR buffers on period ends
| (0 << 6) // bit6 LDT=0: Periodical Loads OF/PH/DC/PR buffers
| (0 << 0); // bit1-0 LDS=0: Reserved
break;
default:
break;
}
}

void PWM_SetPeriod(uint8_t PWM_CH, uint16_t PWM_Period /* uS */){
switch(PWM_CH){
case 1:
// PRレジスタの設定 PR = (Period x PWMxCLK)/Prescale -1
PWM1PRH = (PWM_Period - 1) / 256;
PWM1PRL = (PWM_Period - 1);
break;
case 2:
// PRレジスタの設定 PR = (Period x PWMxCLK)/Prescale -1
PWM2PRH = (PWM_Period - 1) / 256;
PWM2PRL = (PWM_Period - 1);
break;
default:
break;
}
}

void PWM_SetPhase(uint8_t PWM_CH, uint16_t PWM_Phase /* uS */){
switch(PWM_CH){
case 1:
// PHレジスタの設定
PWM1PHH = PWM_Phase / 256;
PWM1PHL = PWM_Phase;
break;
case 2:
// PHレジスタの設定
PWM2PHH = PWM_Phase / 256;
PWM2PHL = PWM_Phase;
break;
default:
break;
}
}

void PWM_SetDuty(uint8_t PWM_CH, uint16_t PWM_Duty /* uS */)
{
if(PWM_Duty < DUTY_MIN) PWM_Duty = DUTY_MIN;
if(PWM_Duty > DUTY_MAX) PWM_Duty = DUTY_MAX;

switch(PWM_CH){
case 1:
// DCレジスタの設定 DC = Duty_Cycle_Ratio (PR + 1) + PH
// =(Duty_Cycle x PWMxCLK)/Prescale + PH
PWM1DCH = PWM_Duty / 256;
PWM1DCL = PWM_Duty;
break;
case 2:
// DCレジスタの設定 DC = Duty_Cycle_Ratio (PR + 1) + PH
// =(Duty_Cycle x PWMxCLK)/Prescale + PH
PWM2DCH = PWM_Duty / 256;
PWM2DCL = PWM_Duty;
break;
default:
break;
}

// Reload Operationの実行
PWM_Reload(PWM_CH);

}

void PWM_Initialize(uint8_t PWM_CH, uint16_t PWM_PR, uint16_t PWM_DC)
{
switch(PWM_CH){
case 1:
// PWM機能の設定
PWM1CON = (1 << 7) // bit7 1: Module is enabled
| (1 << 6) // bit6 1: Output pin is enabled
| (0 << 4) // bit4 0: Active state is high
| (0 << 2); // bit3-2: 0: Standard PWM mode

// PWMx_clockの設定
PWM1CLKCON =(4 << 4) // bit6-4 0: No prescaler
// 4: 1/16
| (1 << 0); // bit1-0 0:Fosc,
// 1:HFINTOSC
// 2:LFINTOSC
break;
case 2:
// PWM機能の設定
PWM2CON = (1 << 7) // bit7 1: Module is enabled
| (1 << 6) // bit6 1: Output pin is enabled
| (0 << 4) // bit4 0: Active state is high
| (0 << 2); // bit3-2: 0: Standard PWM mode

// PWMx_clockの設定
PWM2CLKCON =(4 << 4) // bit6-4 0: No prescaler
// 4: 1/16
| (1 << 0); // bit1-0 0:Fosc,
// 1:HFINTOSC
// 2:LFINTOSC
break;
default:
break;
}

// PRレジスタの設定
PWM_SetPeriod(PWM_CH, PWM_PR);

// PHレジスタの設定 // PH=0 でも設定しないと動作しない
PWM_SetPhase(PWM_CH, 0);

// DCレジスタの設定
PWM_SetDuty(PWM_CH, PWM_DC);

}

// End of PWM16.C

//--------------------------------------------------------------------------
// Analog-to-Digital Converter(ADC) Module
//--------------------------------------------------------------------------

// ADC CLOCK PERIOD: 2.0uS
#if _FREQ == 1 // Fosc = 1MHz -> Fosc/2
#define INIT_ADCS 0b000
#elif _FREQ == 2 // Fosc = 2MHz -> Fosc/4
#define INIT_ADCS 0b100
#elif _FREQ == 4 // Fosc = 4MHz -> Fosc/8
#define INIT_ADCS 0b001
#elif _FREQ == 8 // Fosc = 8MHz -> Fosc/16
#define INIT_ADCS 0b101
#elif _FREQ == 16 // Fosc = 16MHz -> Fosc/32
#define INIT_ADCS 0b010
#elif (_FREQ == 24) && (_16F18313) // Fosc = 24MHz -> Fosc/64(2.7uS)
#define INIT_ADCS 0b110
#elif _FREQ == 32 // Fosc = 32MHz -> Fosc/64
#define INIT_ADCS 0b110
#elif
#error _FREQ is not defined properly.
#endif


void ADC_Initialize(uint8_t fvr_ref)
{
uint8_t INIT_ADPREF = 0; // Vref+ is connected to VDD

if((fvr_ref > 0) && (fvr_ref <= 4))
{
INIT_ADPREF = 0b11; // Vref+ is connected to internal FVR module

// FVR設定
if(fvr_ref == 4) fvr_ref = 3;

FVRCON =(1 << 7) // FVREN: FVR Enable
| (fvr_ref << 0); // ADFVR<1:0> ADC FVR Buffer Gain Selection

while(FVRRDY==0); //

}

ADCON1 =(1 << 7) // ADCF:ADC Result Format Select
| (INIT_ADCS << 4) // ADCS<2:0>:ADC Conversion Clock Select
| (INIT_ADPREF << 0); // ADPREF<1:0>:ADC Positive Voltage Reference Configuration

}

void ADC_SetChannel(uint8_t adc_ch)
{
if(adc_ch <= 3)
{
ADCON0 =(adc_ch << 2)
| (1 << 0); // ADON
}
__delay_us(10);
}

uint16_t ADC_Read(void)
{
ADCON0bits.ADGO = 1; // ADC読み取り開始
while(ADCON0bits.ADGO); // ADC読み取り完了待ち

return ADRES;
}

// End of ADC.C


//--------------------------------------------------------------------------
// 関数・プロトタイプ宣言
//--------------------------------------------------------------------------
// レベル変換
long map(long x, long in_min, long in_max, long out_min, long out_max){
return (x - in_min)*(out_max - out_min)/(in_max - in_min) + out_min;
}

//--------------------------------------------------------------------------
// メイン
//--------------------------------------------------------------------------
void main(void)
{
// PICマイコン設定
SYSTEM_Initialize();

// ADC初期設定
ADC_Initialize(0); // VREF 0:VDD, 4:FVR 4.096V

// PWM初期設定
uint16_t duty = 1500; // Duty 1500uS
PWM_Initialize(1, 20000, duty); // CH: PWM1, 周期:20,000uS, Duty:1,500uS
PWM_Initialize(2, 20000, duty); // CH: PWM2, 周期:20,000uS, Duty:1,500uS

// Loop
while (1)
{
// PWM1: Duty Cycleの更新
ADC_SetChannel(3); // ADC 2:AN2(RA2), 3:AN3(RA4)
ADC_Read();

duty = map(ADRES,0,1023,DUTY_MIN,DUTY_MAX);
PWM_SetDuty(1, duty);

// PWM2: Duty Cycleの更新
ADC_SetChannel(2); // ADC 2:AN2(RA2), 3:AN3(RA4)
ADC_Read();

duty = map(ADRES,0,1023,DUTY_MIN,DUTY_MAX);
PWM_SetDuty(2, duty);

__delay_ms(10);
}
} // End of file

/*


*/

« 【PICマイコン】PIC12F1572で16bitPWM | トップページ | 【16番】RCサーボモーターでKATO HOユニトラック手動ポイントの電動化 »