Q43 PWM
PWM専用のモジュールが搭載されています。すぐに動かせる機構になっています。
使用したDCモータドライバー NJU7386
DCモータを正転、逆転させたいので、DCモータドライブIC、NJU7386を使いました。
NJU7386ブロック図
NJU7386真理値表
フォトカプラでピックマイコンのPWM信号を受け取ります。マイコン基板と、モータ基板のグランドは、分離させています。
NJU7386のINA端子、INB端子にQ43のPWM信号を接続します。
Q43 PWMモジュール概略
DCモータの正逆転を、可変速で制御します。
設定する必要があるレジスタ一覧
PWMコンフィグレーション設定:PWMxSaCFG
レジスタ
PWMクロックソースの選択:PWMCKL
レジスタ
PWMクロックプリスケーラ:PWMxCPRE
レジスタ
PWM周期:PWMxPR
レジスタ
PWMduty時間:PWMxSaP1,PWMxSaP2
レジスタ
PWMのモードは、Left-Alignedにしています。
void pwm1Init(void)
{
PWM1CLK=0x05;//MFINTOSC 500khz
//PWM1PR=0x00EA;//15ms
PWM1PR=0x0138;//20ms
//PWM1PR=0x7EF;//100ms
PWM1S1CFGbits.MODE=0b000;//Left-Alighned mode
//PWM1PR=0x02FFF;
PWM1CPRE=0x20;//prescaler
PWM1S1P1H=0x00;
PWM1S1P1L=0x00;
PWM1S1P2H=0x00;
PWM1S1P2L=0x00;
PWM1S1CFG=0x00;
//Clear PWM1_16BIT period interrupt flag
PIR4bits.PWM1PIF = 0;
//Clear PWM1_16BIT interrupt flag
PIR4bits.PWM1IF = 0;
//Clear PWM1_16BIT slice 1, output 1 interrupt flag
PWM1GIRbits.S1P1IF = 0;
//Clear PWM1_16BIT slice 1, output 2 interrupt flag
PWM1GIRbits.S1P2IF = 0;
//PWM1_16BIT interrupt enable bit
PIE4bits.PWM1IE = 0;
//PWM1_16BIT period interrupt enable bit
PIE4bits.PWM1PIE = 0;
//PPS
//6.PPS
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;//Unlock.
//RB4PPS=0x18;
RC1PPS=0x18;
RC2PPS=0x19;
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;//lock.
PWM1CONbits.EN=1;
}
※PWM割り込みフラグ、割り込み許可を使用していません。
通常のDCモータ制御では、使用しなくても、問題ないと思います。
duty比を変えるためのLDビット
動作中にPWMのduty比を変えて、速度を加減速するとき、PWMxS1P1, PWMxS1P2
レジスタに値をセットします。
ダブルバッファになっているので、ソフトで書きこんでも、即有効になりません。
PWMコントロールレジスタPWMxCON
のLDビットをセットします。
新しい値を更新することをハードに要求します。
ハードは、新しい値を書き込みバッファから読み出して、PWMのdutyカウンタを更新します。
更新完了すると、ハードは、LDビットをクリアします。
ユーザーソフトは、このLDビットがクリアされたことを確認する必要があります。
PWM1S1P1 =dutyWide; //slice1:dutyカウンタ値
PWM1S1P2 =dutyWide; //slice2:dutyカウンタ値
PWM1CONbits.LD=1; //新しいP1,P2の値を更新する
while(PWM1CONbits.LD); //ハードがクリアすれば、設定完了。
PWMモジュールの2つのSlice
PWMのブロック図を見ると、2つのduty比が違うPWM信号を出力できることがわかります。
PWMモジュール一つで、DCモータの正逆転を制御できます。
左右の車輪がついたリモコンカーの場合、PWM1,PWM2モジュールの2つを使えば、2つのDCモータの正逆転を制御することができます。
//PWM1モジュールの加減速
PWM1S1P1 = duty値/15;
PWM1S1P2 = 0;
//PWM2モジュールの加減速
PWM2S1P1 = duty値/15;
PWM2S1P2 = 0;
PWMxS1P1,PWMxS1P2
レジスタは、DCモータの速度を設定するレジスタになります。
今回使用したコード
正逆転の指示を出すためにスーファミコントローラを使用しています。
#ifndef FAMICON_H
#define FAMICON_H
#ifdef __cplusplus
extern "C" {
#endif
#include <xc.h>
#define PS LATAbits.LATA5
#define CLK LATAbits.LATA6
#define DATA1 PORTAbits.RA7
extern uint16_t famiconButtonState;
extern void famiconInit(void);
extern uint16_t getButton(void);
#ifdef __cplusplus
}
#endif
#endif /* FAMICON_H */
#include "famicon.h"
#include "I2C_LCD.h"
uint16_t famiconButtonState;
///@brief コントローラーの初期化
void famiconInit(void)
{
TRISAbits.TRISA7=1;//Data線
}
///@brief ボタン押下状況取得関数
///return ボタン押下16ビットデータ
uint16_t getButton(void)
{
uint16_t w_val;
uint8_t cnt;
w_val=0x0000;
cnt=0;
//最初の1ビット
PS=1;
CLK=1;
__delay_us(10);
PS=0;
CLK=0;
__delay_us(10);
w_val|=DATA1;
w_val<<=1;
cnt++;
//残り15ビットの取得
do{
CLK=1;
__delay_us(10);
CLK=0;
// __delay_us(5);
w_val|=DATA1;
if(cnt!=15)
{
w_val<<=1;
}
cnt++;
}while(cnt!=16);
//受信完了
w_val&=0xFFF0;//ボタンデータのみを、抽出
if(w_val!=0xFFF0)
{
if((w_val&0x8000)==0x0)
{
printf("B ");
}
if((w_val&0x4000)==0x0)
{
printf("Y ");
}
if((w_val&0x2000)==0x0)
{
printf("SELECT ");
}
if((w_val&0x1000)==0x0)
{
printf("START ");
}
if((w_val&0x0800)==0x0)
{
printf("UP ");
}
if((w_val&0x0400)==0x0)
{
printf("DOWN ");
}
if((w_val&0x0200)==0x0)
{
printf("LEFT ");
}
if((w_val&0x0100)==0x0)
{
printf("RIGHT ");
}
if((w_val&0x0080)==0x0)
{
printf("A ");
}
if((w_val&0x0040)==0x0)
{
printf("X ");
}
if((w_val&0x0020)==0x0)
{
printf("L ");
}
if((w_val&0x0010)==0x0)
{
printf("R ");
}
printf("%04x\r",w_val);
}
return w_val;
}
#ifndef Q_PERIPHERAL27Q43_H
#define Q_PERIPHERAL27Q43_H
#ifdef __cplusplus
extern "C" {
#endif
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define Low 0
#define High 1
#define _XTAL_FREQ 32000000
#define BAUDRATE 115200
#define SET_SPBRG_DATA (((unsigned int)((_XTAL_FREQ/4)/BAUDRATE))-1)
#define SET_SPBRG_DATA1 (((unsigned int)((_XTAL_FREQ/16)/BAUDRATE))-1)
//USART---------------------------------------
#define RXUSART_BUFFER_SIZE 20
typedef struct
{
uint8_t buf[RXUSART_BUFFER_SIZE];
uint8_t length;
uint8_t completed;
}_rxUsart;
extern _rxUsart rxUsart;
extern void usartInit(void);
extern void putch(uint8_t);
extern uint8_t getch(void);
//Timer--------------------------------------
typedef struct
{
uint16_t cnt;
bool fg;
}_tm;
extern _tm tm0,tm2;
extern void timer0Init(void);
extern void timer2Init(void);
extern void timer6Init(void);
//IOC------------------------------------------
extern void iocInit(void);
typedef struct
{
bool fg;
}_ioc;
extern _ioc ioc;
//--------------------------------------//
//ADC 12bits
//--------------------------------------//
#define ADC_AverageMode
#define ADC_SamplingNum 8
typedef struct{
uint16_t val[6];
uint32_t sum[6];
uint16_t avr[6];
uint8_t ch;
uint8_t cnt;
bool completed;
}_adc;
extern _adc adcInfo;
extern void adcInit(void);
//--------------------------------------//
//Clock Reference Output
//--------------------------------------//
extern void CLKREF_Init(void);
//--------------------------------------//
//CCP PWM
//--------------------------------------//
extern void CCP1_PWM_Init(void);
//--------------------------------------//
//PWM 16bits
//--------------------------------------//
typedef struct
{
uint16_t val;
}_pwm;
extern _pwm pwm1,pwm2;
extern void pwm1Init(void);
extern void pwm2Init(void);
extern void pwm3Init(void);
#ifdef __cplusplus
}
#endif
#endif /* Q_PERIPHERAL27Q43_H */
_pwm pwm1,pwm2;
//******************************************************//
// * PWM1初期化
// *****************************************************//
void pwm1Init(void)
{
PWM1CLK=0x05;//MFINTOSC 500khz
//PWM1PR=0x00EA;//15ms
PWM1PR=0x0138;//20ms
//PWM1PR=0x7EF;//100ms
//PWM1PR=0x02FFF;
PWM1CPRE=0x20;//prescaler
PWM1S1P1H=0x00;
PWM1S1P1L=0x00;
PWM1S1P2H=0x00;
PWM1S1P2L=0x00;
PWM1S1CFG=0x00;
//Clear PWM1_16BIT period interrupt flag
PIR4bits.PWM1PIF = 0;
//Clear PWM1_16BIT interrupt flag
PIR4bits.PWM1IF = 0;
//Clear PWM1_16BIT slice 1, output 1 interrupt flag
PWM1GIRbits.S1P1IF = 0;
//Clear PWM1_16BIT slice 1, output 2 interrupt flag
PWM1GIRbits.S1P2IF = 0;
//PWM1_16BIT interrupt enable bit
PIE4bits.PWM1IE = 0;
//PWM1_16BIT period interrupt enable bit
PIE4bits.PWM1PIE = 0;
//PPS
//6.PPS
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;//Unlock.
//RB4PPS=0x18;
RC1PPS=0x18;
RC2PPS=0x19;
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;//lock.
PWM1CONbits.EN=1;
pwm1.val=0x0000;
}
//******************************************************//
// * PWM2 初期化
// *****************************************************//
void pwm2Init(void)
{
PWM2CLK=0x05;//MFINTOSC 500khz
PWM2PR=0x00EA;//15ms
//PWM2PR=0x7EF;//100ms
//PWM2PR=0x02FFF;
PWM2CPRE=0x20;//prescaler
PWM2S1P1H=0x00;
PWM2S1P1L=0x00;
PWM2S1CFG=0x00;
//Clear PWM1_16BIT period interrupt flag
PIR5bits.PWM2PIF = 0;
//Clear PWM1_16BIT interrupt flag
PIR5bits.PWM2IF =0;
//Clear PWM1_16BIT slice 1, output 1 interrupt flag
PWM2GIRbits.S1P1IF=0;
//Clear PWM1_16BIT slice 1, output 2 interrupt flag
PWM2GIRbits.S1P2IF =0;
//PWM1_16BIT interrupt enable bit
PIE5bits.PWM2IE=0;
//PWM1_16BIT period interrupt enable bit
PIE5bits.PWM2PIE=0;
//PPS
//6.PPS
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;//Unlock.
RB0PPS=0x1A;
RC5PPS=0x1B;
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;//lock.
PWM2CONbits.EN=1;
pwm2.val=0x0000;
}
/*
* File: main.c
* Author: yutak
*
* Created on 2022/12/29, 18:47
*/
// PIC18F27Q43 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FEXTOSC = OFF // External Oscillator Selection (Oscillator not enabled)
#pragma config RSTOSC = HFINTOSC_64MHZ// Reset Oscillator Selection (HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1)
// CONFIG2
#pragma config CLKOUTEN = OFF // Clock out Enable bit (CLKOUT function is disabled)
#pragma config PR1WAY = OFF // PRLOCKED One-Way Set Enable bit (PRLOCKED bit can be set and cleared repeatedly)
#pragma config CSWEN = ON // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
// CONFIG3
#pragma config MCLRE = EXTMCLR // MCLR Enable bit (If LVP = 0, MCLR pin is MCLR; If LVP = 1, RE3 pin function is MCLR )
#pragma config PWRTS = PWRT_OFF // Power-up timer selection bits (PWRT is disabled)
#pragma config MVECEN = ON // Multi-vector enable bit (Multi-vector enabled, Vector table used for interrupts)
#pragma config IVT1WAY = ON // IVTLOCK bit One-way set enable bit (IVTLOCKED bit can be cleared and set only once)
#pragma config LPBOREN = OFF // Low Power BOR Enable bit (Low-Power BOR disabled)
#pragma config BOREN = SBORDIS // Brown-out Reset Enable bits (Brown-out Reset enabled , SBOREN bit is ignored)
// CONFIG4
#pragma config BORV = VBOR_1P9 // Brown-out Reset Voltage Selection bits (Brown-out Reset Voltage (VBOR) set to 1.9V)
#pragma config ZCD = OFF // ZCD Disable bit (ZCD module is disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON)
#pragma config PPS1WAY = OFF // PPSLOCK bit One-Way Set Enable bit (PPSLOCKED bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = ON // Low Voltage Programming Enable bit (Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored)
#pragma config XINST = OFF // Extended Instruction Set Enable bit (Extended Instruction Set and Indexed Addressing Mode disabled)
// CONFIG5
#pragma config WDTCPS = WDTCPS_31// WDT Period selection bits (Divider ratio 1:65536; software control of WDTPS)
#pragma config WDTE = OFF // WDT operating mode (WDT Disabled; SWDTEN is ignored)
// CONFIG6
#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
#pragma config WDTCCS = SC // WDT input clock selector (Software Control)
// CONFIG7
#pragma config BBSIZE = BBSIZE_512// Boot Block Size selection bits (Boot Block size is 512 words)
#pragma config BBEN = OFF // Boot Block enable bit (Boot block disabled)
#pragma config SAFEN = OFF // Storage Area Flash enable bit (SAF disabled)
#pragma config DEBUG = OFF // Background Debugger (Background Debugger disabled)
// CONFIG8
#pragma config WRTB = OFF // Boot Block Write Protection bit (Boot Block not Write protected)
#pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers not Write protected)
#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not Write protected)
#pragma config WRTSAF = OFF // SAF Write protection bit (SAF not Write Protected)
#pragma config WRTAPP = OFF // Application Block write protection bit (Application Block not write protected)
// CONFIG10
#pragma config CP = OFF // PFM and Data EEPROM Code Protection bit (PFM and Data EEPROM code protection disabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "Q_peripheral27Q43.h"
#include "Q_interrupt27Q43.h"
#include "Q_initialize.h"
#include "Q_I2C1.h"
#include "I2C_LCD.h"
#include "stringFormat.h"
#include "Q_SPI2.h"
#include "MCP23Sxx.h"
#include "famicon.h"
void portInit(void);
void oscillatorInit(void);
void vicInit(void);
void main(void)
{
int16_t speed=0;
uint8_t counter=0;
uint16_t ledVal;
uint8_t lcd_length;
//CPUハード初期化-----------------------
portInit();
oscillatorInit();
vicInit();
//周辺機能初期化--------------------------------
timer0Init();
pwm1Init();
pwm2Init();
iocInit();
usartInit();
CLKREF_Init();
I2C1_Init();
SPI2_Init();
//device initialize
LCD_Init(LcdDeviceAdd);//100kHzで初期化
//Clock Reference Output//I2C用クロックソース400KHz
I2C1CLK = 0b00100;
MCP23S17_Init(MCP23S08_Device1Add);
famiconInit();
printf("START\n");
lcd_length=sprintf(LcdTextBuf,"START");
LCD_Printf(LcdDeviceAdd,LcdTextBuf,lcd_length,0x80);
INTCON0bits.GIE=1;//Enable all masked interrupts
ledVal=0x55;
while(1)
{
//Timer0 interrupt process-------
if(tm0.fg)
{
tm0.fg=false;
//Interrupt ADCC process----------
SPI2_b3Write(MCP23S17_Device1Add,0x0A,ledVal); //GPIOA
SPI2_b3Write(MCP23S17_Device1Add,0x1A,ledVal);
if(ledVal==0x55)
{
ledVal=0xAA;
}else
{
ledVal=0x55;
}
T0CON0bits.EN=1;
PIE3bits.TMR0IE=1;
}
//Usart interrupt process--------
if(rxUsart.completed)
{
rxUsart.completed=false;
printf("echo:%s\n",rxUsart.buf);
rxUsart.length=0;
PIE4bits.U1RXIE=1;
}
//Interrupt On Change process--------
if(ioc.fg)
{
ioc.fg=false;
printf("Switch ON:%d\n",counter);
PORTBbits.RB1=~PORTBbits.RB1;
counter++;
PIE0bits.IOCIE=1;
}
//ここからPWM1,2を制御------------------
famiconButtonState = getButton();//スーファミコントローラ押下取得
if((famiconButtonState&0x0020)==0x0000)//L
{//減速
if(speed>=100)
speed-=100;
}
if((famiconButtonState&0x0010)==0x0000)//R
{//加速
speed+=100;
if(speed>4000)
{
speed=4000;
}
}
lcd_length=sprintf(LcdTextBuf,"%04d",speed);
LCD_Printf(LcdDeviceAdd,LcdTextBuf,lcd_length,0xC0);
pwm1.val=speed;
pwm2.val=speed;
if((famiconButtonState&0x0800)==0x0000)//UP 前進
{
PWM1S1P1 = 0;
PWM1S1P2 = pwm1.val/15;//4095/15=273(0x111)<312(0x138)
PWM2S1P1 = 0;
PWM2S1P2 = pwm2.val/15;
}
if((famiconButtonState&0x0400)==0x0000)//DOWN 更新
{
PWM1S1P1 = pwm1.val/15;
PWM1S1P2 = 0;
PWM2S1P1 = pwm2.val/15;
PWM2S1P2 = 0;
}
if((famiconButtonState&0x0200)==0x0000)//LEFT 左折
{
PWM1S1P1 = pwm1.val/15;
PWM1S1P2 = 0;
PWM2S1P1 = 0;
PWM2S1P2 = pwm2.val/15;
}
if((famiconButtonState&0x0100)==0x0000)//RIGHT 右折
{
PWM1S1P1 = 0;
PWM1S1P2 = pwm1.val/15;
PWM2S1P1 = pwm2.val/15;
PWM2S1P2 = 0;
}
if((famiconButtonState==0xFFF0))//No push button 静止状態
{
PWM1S1P1 =0;
PWM1S1P2 =0;
PWM2S1P1 =0;
PWM2S1P2 =0;
}
PWM1CONbits.LD=1;
while(PWM1CONbits.LD);
PWM2CONbits.LD=1;
while(PWM2CONbits.LD);
}
return;
}
void oscillatorInit(void)
{
//オシレータ設定----------------
OSCCON3bits.CSWHOLD=1;//Hold
OSCCON1bits.NDIV=1;//64Mhz/2=32Mhz;
while(!OSCCON3bits.NOSCR);
while(!PIR0bits.CSWIF);//ready state
PIR0bits.CSWIF=0;
OSCCON3bits.CSWHOLD=0;
while(!OSCCON3bits.ORDY);
}
void portInit(void)
{
//ポート設定----------------------
PORTA=0x00;
LATA=0x00;
ANSELA=0x00;
TRISA=0x00;
PORTB=0x00;
LATB=0x00;
ANSELB=0x00;
TRISB=0x00;
PORTC=0x00;
LATC=0x00;
ANSELC=0x00;
TRISC=0x00;
//PPS---------------------
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;
//I2C1-------------------------
//RC4 for SDA
RC4PPS=0x38;
I2C1SDAPPS=0x14;
//RC3 for SCL
RC3PPS = 0x37;
I2C1SCLPPS=0x13;
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;
ODCONCbits.ODCC3=1;
ODCONCbits.ODCC4=1;
RC3I2Cbits.TH=1;
RC4I2Cbits.TH=1;
}
void vicInit(void)
{
//割り込みテーブルaddress設定----------------------------------
INTCON0bits.GIE=0;//Enable all masked interrupts
IVTLOCK=0x55;
IVTLOCK=0xAA;
IVTLOCKbits.IVTLOCKED=0;
IVTBASE = 0x000008;
IVTLOCK=0x55;
IVTLOCK=0xAA;
IVTLOCKbits.IVTLOCKED=1;
}