dsPIC30FやdsPIC33EPから、dsPIC33CKへの移行の覚書です
大きな違いは?
独断と偏見で恣意的な比較表
dsPIC30F | dsPIC33EP | dsPIC33CK | |
MAX速度 | 30MIPS | 70MIPS | 100MIPS |
Core | MUL,Divider | MUL,Divider、4×割り込み退避エリア | |
汎用タイマ | 16bit×3 | 16bit×3 |
32bit×1(CCPの汎用タイマ×2)、PTG×1 |
ADC |
12bit 200Ksps |
12bit×2+1(shared), 3.25Msps |
12bit×2+1(shared), 3.25Msps |
DAC | non | 12bit×2 | 12bit×3 |
PWM | 16bit×3 |
HSPWM 16bit×3,up to 1.04㎱ |
HSPWM 16bit×8,up to 250ps |
EEPROM | 1Kbyte/non | non | non |
other Function | UART,SPI,I2C | UART,SPI,I2C,PGA | UART,SPI,I2C,CAN FD,QEI,CLC,DMA |
電流 | 大 | 小 | 小小 |
要は、高速で高機能かつ低消費電流です。
dsPIC33CK は汎用タイマとしてTimer1一つしかありませんが、名称がかわるだけでCCPのタイマを従来のTimer2、Timer3としてつかえます。またトリガタイミング発生用としてPTG(プログラマブルタイミングジェネレータ)があります。
また一つだけ(?)、dsPIC30Fにあって、dsPIC33CK/EP にないもの
EEPROM
です。
フラッシュで代用できないこともないですが、書き込み手順が面倒なのと、ICDで書き込みエリアを制限しないと、書き込み/デバッグのたびに消去されてしまうし、MPLABXで書き込みエリア制限の設定がなかなかうまくいかない、という状態です。
都合の悪いことに、うちで作ったdsPIC30Fの基板はしっかりと内蔵EEPROMをつかっているのである。外付けEEPROMは必須です。
◆各初期設定覚書◆
各コードには冗長なものが含まれています。各自の責任においてご使用ください。また、リファレンスマニュアルから抜粋したコードは、最初にレジスタクリアごにビットをセットしていますが、パワーオンではクリアされても、ウォッチドッグタイムアウトなどでは必要かもしれませんので、各自でセットすることをお勧めします。
クロックの設定
高速クロックは、Coreに供給するマスターと補助を設定する。今回は内蔵FRCを使う初期設定。
// *********************
// クロック関係初期設定
// *********************
void initCLK(void)
{
// Fpll=8MHz*1*125/5/1=200MHz,Fosc=100MHz,Fp=50MHz 50MIPS
CLKDIVbits.PLLPRE = 1; // N1=1
PLLFBDbits.PLLFBDIV = 125; // M = 125
PLLDIVbits.POST1DIV = 5; // N2=5
PLLDIVbits.POST2DIV = 1; // N3=1
// Initiate Clock Switch to Primary Oscillator with PLL (NOSC=0b011)
__builtin_write_OSCCONH(0x01);
__builtin_write_OSCCONL(OSCCON | 0x01);
// Wait for Clock switch to occur
while (OSCCONbits.OSWEN!= 0);
while (OSCCONbits.LOCK!=1);
// AFVCO = 400MHz AFPLLO = 50MHz using 8 MHz internal FRC
// Configure the source clock for the APLL
ACLKCON1bits.FRCSEL = 1; // Select internal FRC as the clock source
// Configure the APLL prescaler, APLL feedback divider, and both APLL postscalers.
ACLKCON1bits.APLLPRE = 1; // N1 = 1
APLLFBD1bits.APLLFBDIV = 50; // M = 50:400MHz(min)
APLLDIV1bits.APOST1DIV = 2; // N2 = 5:200MHz
APLLDIV1bits.APOST2DIV = 4; // N3 = 4:50MHz
// Enable APLL
ACLKCON1bits.APLLEN = 1;
}
EEPROMは外付け24LCxxにしよう
I2CはPPS適用外で、ピンアサイン固定。I2Cの初期化はdsPIC33EPとほぼ同じ。
void initI2C(void) {
I2C1CONLbits.I2CEN = 1; // Enable I2C Module
I2C1CONLbits.A10M = 0; // 7-bit slave address/data
I2C1CONLbits.DISSLW = 1; // Slew rate control disabled
I2C1BRG = 197; // 400kHz
// I2C1CONLbits.SEN = 1;
}
汎用タイマTimer1の初期化(100usサイクル周期割り込み用)
// ************************
// Timer1初期設定
// ************************
#define TM1_CNT 625 // Fp=50MHz ∴N=50MHz/10KHz/8
void initTM(void) {
TMR1 = 0; // クリヤ TM1レジスタ
PR1 = TM1_CNT; // カウンタ値を設定
T1CONbits.TON = 0; // タイマ停止後から始動
T1CONbits.TCKPS = 1; // プリスケーラ値 1:8
T1CONbits.TGATE = 0;
T1CONbits.TCS = 0; // Fp選択
T1CONbits.TECS = 0x2; // Fosc(no use ∴TCS=0)
T1CONbits.TON = 1; // TM1始動
}
IOポートとPPSを設定しておく
LATxはその都度変わります。PWM・I2CやCANFDなどの固定ペリフェラル以外はPPSで設定する。
// *** InitIO ***
void initIO(void) {
LATA = 0x0; // DD Con STBY OFF
LATB = 0x0;
LATC = 0x0;
ANSELA= 0b0000000000001111; // RA4:digital other:analog
ANSELB= 0x0; // RB all digital
TRISA = 0b0000000000001111; // RA0-1:AN,other out
TRISB = 0b0000000010011100; // RB2-4,7:input,other out
CNPUA = 0b0000000000010000; // RA2(SW2):pullup
CNPDB = 0b1111110000000000; // RB10-15:pulldown
CNPUB = 0b0000000000000000; // PB1-4:pullup
CNPUC = 0b0000000000000001; // PC0(SW1):pullup
RPINR18bits.U1RXR = 52; // RPIN18(U1RXT) -> PR52
RPOR10bits.RP53R = 1; // RP53 -> U1TX
}
いよいよADCの設定
ADCはPWMトリガ1でトリガをかけ、ADC1で割り込みをかける設定。
コードはDS70005213Gより適用。dsPIC33EPで必要だったADCのキャリブレーションは不要となっている。
// *** InitADC ***
void initADC(void) {
ANSELA = 0x0003; // AN0,1 analog
ADCON1L = 0x0000; // AD off
ADCON1Hbits.FORM = 0; // integer format
ADCON1Hbits.SHRRES = 0x3; // SHARED ADC:Integer,12bit
ADCON2Hbits.SHRSAMC = 15; // SHARED ADC Sample Time:17*TADCORE
ADCON3Lbits.REFSEL = 0; // AVdd as voltage reference
ADCON3Hbits.CLKSEL = 0; // Fosc(100MHz)を使う(ADCORExと組み合わせてMax70MHz))
ADCON3Hbits.CLKDIV = 0; // no clock divider
ADCON4Hbits.C0CHS = 0; // CORE0:AN0
ADCON4Hbits.C1CHS = 0; // CORE1:AN1
ADCORE0Lbits.SAMC = 0; // Core0 delay:TADCORE*2
ADCORE1Lbits.SAMC = 0; // Core1 delay:TADCORE*2
ADCORE0Hbits.ADCS = 0; // clock divider (1:2)
ADCORE1Hbits.ADCS = 0; // clock divider (1:2)
ADCORE0Hbits.RES = 3; // Core 0 ADC Core in 12-bit resolution mode
ADCORE1Hbits.RES = 3; // Core 1 ADC Core in 12-bit resolution mode
ADMOD0Lbits.SIGN0 = 0; // AN0/RA0
ADMOD0Lbits.DIFF0 = 0; // AN0/RA0
ADMOD0Lbits.SIGN1 = 0; // AN1/RA1
ADMOD0Lbits.DIFF1 = 0; // AN1/RA1
ADMOD0Lbits.SIGN2 = 0; // AN2/RB7
ADMOD0Lbits.DIFF2 = 0; // AN2/RB7
ADIELbits.IE0 = 1; // enable interrupt for AN0
ADIELbits.IE1 = 1; // enable interrupt for AN1
_ADCAN0IF = 0; // clear interrupt flag for AN0
_ADCAN0IE = 1; // enable interrupt for AN0
_ADCAN1IF = 0; // clear interrupt flag for AN1
_ADCAN1IE = 0; // enable interrupt for AN1
ADTRIG0Lbits.TRGSRC0 = 0x4; // AN0:PWM1 trigger
ADTRIG0Lbits.TRGSRC1 = 0x4; // AN1:PWM1 trigger
ADCON1Lbits.ADON = 1; // Turn on the ADC module now.
ADCON5L = 0; // 各COREをパワーオンする
ADCON5Lbits.C0PWR = 1 ; // Turn on analog power for dedicated core 0
while(ADCON5Lbits.C0RDY == 0);
ADCON3Hbits.C0EN = 1 ; // Dedicated ADC Core 0 Enable
ADCON5Lbits.C1PWR = 1 ; // Turn on analog power for dedicated core 1
while(ADCON5Lbits.C1RDY == 0);
ADCON3Hbits.C1EN = 1 ; // Dedicated ADC Core 1 Enable
ADCON5Lbits.SHRPWR = 1 ; // shared core ON
while(ADCON5Lbits.SHRRDY == 0);
ADCON3Hbits.SHREN = 1 ; // Shared ADC Core Enable
while(ADCON5Lbits.SHRRDY == 0); // shared core ready?
}
ラスボス、HSPWMの設定
もっともシンプルな差動PWMですが、動作するのに一番時間がかかりました。
// *** InitPWM ***
void initPWM(void) {
// 単純 相補PWM
// PWM control register configuration
PCLKCONbits.MCLKSEL = 2; // MCLKSEL FPLLO:200MHz
MPER = 10000; // Master Period register
// Initial Duty Setup
PG1DC = 1000; // 25% duty
PG2DC = 1000; // 50% duty
PG3DC = 1000; // 75% duty
PG1CONLbits.CLKSEL = 1;
PG1CONLbits.MODSEL = 0; //Independent edge triggered mode
PG1CONH = 0x0000; // Complementary mode, PWM1H and PWM1L output pins, output pins are active high
PG1CONHbits.MPERSEL = 1; // MPERselect(master periodを使う)
PG1IOCONH = 0x000C; // PWM uses PG1DC, PG1PER, PG1PHASE registers, not broadcast UPDATE status bit state or EOC signal
// Update the data registers at start of next PWM cycle (SOC), Single Trigger mode, (SOC) = local EOC, Write to DATA REGISTERS
// PG1PER = 10000; // PWM frequency is 100kHz
PG1PHASE = 0; // Phase offset in rising edge of PWM
PG1DTH = 40; // Dead time on PWMH:500ns
PG1DTL = 40; // Dead time on PWML
PG2CONLbits.CLKSEL = 1;
PG2CONLbits.MODSEL = 0; // Independent edge triggered mode
PG2CONH = 0x0000; // Complementary mode, PWM1H and PWM1L output pins, output pins are active high
PG2CONHbits.MPERSEL = 1; // MPERselect(master periodを使う)
PG2IOCONH = 0x000C; // PWM uses PG1DC, PG1PER, PG1PHASE registers, not broadcast UPDATE status bit state or EOC signal
// Update the data registers at start of next PWM cycle (SOC), Single Trigger mode, (SOC) = local EOC, Write to DATA REGISTERS
// PG2PER = 10000; // PWM frequency is 100kHz
PG2PHASE = 0; // Phase offset in rising edge of PWM
PG2DTH = 40; // Dead time on PWMH
PG2DTL = 40; // Dead time on PWML
PG3CONLbits.CLKSEL = 1;
PG3CONLbits.MODSEL = 0; // Independent edge triggered mode
PG3CONH = 0x0000; // Complementary mode, PWM1H and PWM1L output pins, output pins are active high
PG3CONHbits.MPERSEL = 1; // MPERselect(master periodを使う)
PG3IOCONH = 0x000C; // PWM uses PG1DC, PG1PER, PG1PHASE registers, not broadcast UPDATE status bit state or EOC signal
// Update the data registers at start of next PWM cycle (SOC), Single Trigger mode, (SOC) = local EOC, Write to DATA REGISTERS
// PG3PER = 10000; // PWM frequency is 100kHz
PG3PHASE = 0; // Phase offset in rising edge of PWM
PG3DTH = 40; // Dead time on PWMH
PG3DTL = 40; // Dead time on PWML
// CMBTRIGLbits.CTA1EN = 1;
PG1TRIGA = 5000; // Initialize PWM GENERATOR 1 TRIGGER A REGISTER
PG1TRIGB = 0; // Initialize PWM GENERATOR 1 TRIGGER B REGISTER
PG1TRIGC = 0; // Initialize PWM GENERATOR 1 TRIGGER C REGISTER
PWMEVTAbits.EVTASEL=0b1000; //ADCTrigger1 signal
PWMEVTAbits.EVTAPGS=0b000; //PWM generator #1
PG1EVTL =0x0000;
PG1EVTLbits.ADTR1PS = 0; // postscale 1:1
PG1EVTLbits.ADTR1EN3 = 0; // PG1TRIGAでコンペア
PG1EVTLbits.ADTR1EN2 = 0; // PG1TRIGAでコンペア
PG1EVTLbits.ADTR1EN1 = 1; // PG1TRIGAでコンペア
PG1EVTLbits.PGTRGSEL = 0; // Trigger1:PGxTRIGA event
PG1EVTH =0x0000;
PG1EVTHbits.IEVTSEL = 0x1; // PWM割り込み使用しないのでN.A.
PG1EVTHbits.ADTR2EN1 =1; // Trigger2未使用だが仮設定
PG1EVTLbits.UPDTRG = 1; // update Trigger Select:PG1DC
PG2EVTLbits.UPDTRG = 1; // update Trigger Select:PG2DC
PG3EVTLbits.UPDTRG = 1; // update Trigger Select:PG3DC
PG1CONLbits.ON = 1; // PWM1 module is enabled(PWM全部セットしてからONする)
PG2CONLbits.ON = 1; // PWM2 module is enabled
PG3CONLbits.ON = 1; // PWM3 module is enabled
}
差動PWMでADCトリガをかけるので、PG1TRIGAをPWMピリオドの真ん中の5000=周期10000/2とする(要微調)。
また、PG1CON.ON=1を途中で行うと、その後のセットが無効になるので注意!