O型のまこさん

O型のまこさん

趣味でいろいろ作った備忘録

4-6.PIC16F877A+L6470によるモータ駆動のサンプルコード

■PIC16F877AとL6470によるスッテッピングモータ制御のサンプルコード
com_def.h    :共通定義文
uart.h          :シリアル通信(RS-232C)定義文
uart.c          :シリアル通信(RS-232C)関数
877A_serv.c  :メインタスク処理

■com_def.h   ************************************************************
// 定義文
#define ON      1 // ON 定義
#define OFF     0 // OFF定義
#define I_8  signed char //  8bit -128~127
#define U_8  unsigned char //  8bit 0~255
#define I16  signed int // 16bit -32,768 ~ +32,767
#define U16  unsigned int // 16bit 0 ~ 65,535
#define I32  signed long // 32bit -2,147,483,648 ~+2,147,483,647
#define U32  unsigned long // 32bit 0 ~ 4,294,967,295
#define F32  double // 32bit メモリを消費量が激しいので通常は使用しない
#define CR    0x0D // ASCII 制御コード
#define LF    0x0A // ASCII 制御コード

■uart.h      ************************************************************
#ifndef __UART_H__
#define __UART_H__
 
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 // 4MHz                        ★  4000000:正常動作確認(システムクロックを記述:今回は外部4MHz発振子使用)
#endif
 
#define BAUDRATE    14400  // 14400 bps                   ★ 14400bps:正常動作確認
#define TX9_RX9_BIT 0      // 0: 8bit, 1: 9bit            ★        0:正常動作確認
#define BRGH_BIT    1      // 0/1:(低速/高速)サンプル指定 ★        1:正常動作確認 
 
// 9-bit Transmit Enable bit -> 0:8-bit Transmission  1:9-bit Transmission
#if TX9_RX9_BIT == 1
#define TX9_RX9_DATA 0x40
#else
#define TX9_RX9_DATA 0x00
#endif

// High Baud Rate Select bit -> 0:Low speed  1:High speed
//  PIC16F877A.pdfの115page TABLE 10-1の式により、ボーレートからSPBRG値を逆算
#if BRGH_BIT == 1
#define BRGH_DATA 0x04
#define SPBRG_DATA *1 // BRGH=1(High Speed)時のSPBRG値
#else
#define BRGH_DATA 0x00
#define SPBRG_DATA *2 // BRGH=0(Low Speed)時のSPBRG値
#endif
 
// プロトタイプ宣言
void initUART();
void putch(unsigned char byte);
unsigned char getch();
unsigned char getche();
 
#endif  // __UART_H__

■uart.c      ************************************************************
#include <pic.h>
#include <stdio.h>
 
#include "uart.h"
 
void initUART( void )
{
PIE1bits.RCIE   = 1 ; // USART割込み受信を有効
PIR1bits.RCIF   = 0 ; // USART割込み受信フラグの初期化
TXSTA = (TX9_RX9_DATA | BRGH_DATA | 0x20); // TX9_RX9_DATA,BRGH_DATAはuart.hで定義、0x020:Transmit enabled 
RCSTA = (TX9_RX9_DATA | 0x90); // TX9_RX9_DATAはuart.hで定義、0x90(SPEN->Serial port enabled, CREN->Enables continuous receive
SPBRG = SPBRG_DATA; // PIC16F88ではTXSTA,RCSTAの前で処理すると正常動作しない(PIC16F877Aでは現在評価中)
}
 
void putch(unsigned char byte)
{
while(!PIR1bits.TXIF){}
TXREG = byte;
}
 
unsigned char getch( void )
{
while(!PIR1bits.RCIF){}
return RCREG;
}
 
unsigned char getche( void )
{
unsigned char c;

c = getch();
putch(c);

return c;
}

■877A_serv.c ************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <pic.h>
#include "com_def.h"
#include "uart.h"
// タスク制御用パラメータ
// ■割り込み処理用のカウンタ初期値設定
// 割り込み周期 = (0xffff - TMR1)*1 [μsec]
//                |64536->65536
//  TMR1入力クロック  |    4MHz 
//  割り込み      |   1msec 
//  初期値(カウンタ値)|64536(1000)
//     16進表示   |   0xfc18  
#define P_TIMER_H 0xfc //                |    0xfc   
#define P_TIMER_L 0x2a //                |    0x18   
// カウンタ初期値補正値
//   ・default:4MHz:0xfc18 10MHz:0xf63c
//   ・4MHz水晶発振子での計測による補正値:0xfc2a
//   ・内部クロック  での計測による補正値:0xfc23
#define D_TIMER   0xfc2a // PIC16F877Aでは2byte処理が可能なので2bytにまとめる。
#define D_INT_TSK 10 // タスク周期 = 割り込み周期*D_INT_TSK   (0~255)
// 受信処理関連定義文
#define D_REC_RBUFSIZE  10 // 受信リングバッファサイズ
#define D_REC_CMDSIZE    9 // 受信命令判定サイズ
#define D_PRT_SIZE      16 // 
#define P_CHECK         10 // 10msec×P_CHECK毎にLEDが点滅(max 65536まで:U16変数のため)

// ビットフィールド変数宣言
struct {
U16 task_exec_flg : 1; // 00:タスク演算終了確認フラグ(ON/OFF→未/済)
U16 rec_flg       : 1; // 01:シリアル受信フラグ
U16 LED           : 1; // 02:LED点灯指令
U16 flick_flg     : 1; // 04:
} bit1;


// 変数宣言
I_8 recRing[D_REC_RBUFSIZE]; // 受信リングバッファ
I_8 recRingCtr; // 受信リングバッファカウンタ
I_8 rec_cmd[D_REC_CMDSIZE]; // 受信セット
U16 alt500m_ctr; // 500msec周期処理用カウンタ
I16 Count ; // タイマーの割込み発生回数をカウントする変数

// 関数のプロトタイプ宣言
void recv_fun( void );
void interrupt InterTimer( void );
void init_fun( void );
void input__fun( void );
void output_fun( void );
void task___fun( void );
U_8  SPIMsCom( U_8 );
void SPIwrite_3byteCmd(U_8, U32);
void SPIwrite_2byteCmd(U_8, U16);
void SPIwrite_1byteCmd(U_8, U_8);
U32  SPIread_3byteCmd(U_8, U32);
U16  SPIread_2byteCmd(U_8, U16);
U_8  SPIread_1byteCmd(U_8, U_8);

// CONFIGURATION WORD definitions  (PIC16F887A)
#pragma config CP     = 0x1 // Code protection off
#pragma config DEBUG  = 0x1 // In-Circuit Debugger disabled, RB6 and RB7 are general purpose I/O pins
#pragma config WRT    = 0x2 // 0000h to 00FFh write-protected
#pragma config CPD    = 0x1 // Data EEPROM code protection off
#pragma config LVP    = 0x0 // RB3 is digital I/O
#pragma config BOREN  = 0x1 // Brown-out Reset enabled (1:enabled / 0:disabled )
#pragma config PWRTE  = 0x0 // Power-up Timer enabled  (0:enabled / 1:disabled )
#pragma config WDTE   = 0x0 // Watchdog Timer disabled (1:enabled / 0:disabled )
#pragma config FOSC   = HS // oscillator (  4MHz~ 20MHz)

// InterTimer() 割込みの処理
void interrupt InterTimer( void )
{
I_8 i,s;
// タイマー割り込み発生時の処理
if(PIR1bits.TMR1IF == 1) // タイマー1の割込み発生か?
{
PIR1bits.TMR1IF = 0;     // タイマー1割込フラグをリセット
TMR1  = D_TIMER; // タイマ1初期化(本値~65536までカウントアップ
Count++ ; // 割込み発生回数をカウント
if( Count >= D_INT_TSK ) // 割込みをD_INT_TSK回毎にタスク実行フラグをON
{
bit1.task_exec_flg = ON; // タスク演算終了確認フラグをON(未実行状態へ)
Count = 0 ;
}
}
// 受信割り込み発生時の処理
if (PIR1bits.RCIF == 1) // 割込みはUSART通信の受信か?
{
if( RCREG == CR ) // コマンド指令の末尾にCR受信時のみ受信処理を実施
{
// 受信リングバッファを受信セットに代入
for(i=0;i<D_REC_CMDSIZE;i++)
{
// recRingCtr = 0の場合
s = ( recRingCtr - 1) - i;
if( s < 0 ){ s += D_REC_RBUFSIZE; }
rec_cmd[i] = recRing[s];
}
bit1.rec_flg = ON; // 受信フラグをセット(受信処理を実行)
}
else
{
// 受信データをリングバッファに格納
recRing[recRingCtr] = RCREG;
recRingCtr ++;
if( recRingCtr >= D_REC_RBUFSIZE ){ recRingCtr = 0; }
}
PIR1bits.RCIF = 0 ;           // 割込み受信フラグをリセット
}
}

// 初期化処理
void init_fun( void )
{
int i;
U16 tempU16;
U_8 tempU_8;
PORTA  = 0x00; PORTB  = 0x00; PORTC  = 0x00; PORTD  = 0x00;
CMCON  = 0x07 ; // コンパレータは使用しない(内容確認)
TRISA  = 0b00111000; // RA5~RA0の入出力設定 0:出力 1:入力 (RA0とRA1は出力のため0)
TRISB  = 0b11111111; // RB7~RB0の入出力設定 0:出力 1:入力
TRISC  = 0b10010111; // RC7~RC0の入出力設定 0:出力 1:入力 (RC6はシリアル出力[SPI] RC3,SCK,RC4,RC5
TRISD  = 0b11111000; // RD7~RD0の入出力設定 0:出力 1:入力 (RD0とRD1はSPIのCSとしてDOとして使用)
ADCON1 = 0b00000110; // AN0~AN7をデジタル入力に設定(PCFG1と2を1)
T1CON  = 0b00000001; // 内部クロックでTIMER1をカウントする、プリスケーラー 1:1
TMR1   = D_TIMER; // タイマ1初期化(本値~65536までカウントアップ
PIR1   = 0b00000000; // PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF
PIE1   = 0b00000001; // PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE
INTCON = 0b11000000; // GIE PEIE TMR0IE INTE RBIE TMR0IF INTF RBIF
SSPSTAT= 0b11000000; // 【SPI設定】SMP CKE D/A P S R/W UA BF  ( D/A,P,S,R/W,UAはI2C通信用 )
SSPCON = 0b00110010; // 【SPI設定】WCOL SSPOV SSPEN CKP SSPM3 SSPM2 SSPM1 SSPM0
initUART(); // 調歩同期式シリアル通信設定
__delay_ms(200);
printf("\r\n");
printf("  Init L6470 Parameter\r\n");
SPIwrite_2byteCmd(0x05,    20); // ACC        [0- 4096]:加速度係数
SPIwrite_2byteCmd(0x06,    20); // DEC        [0- 4096]:減速度係数
SPIwrite_2byteCmd(0x07,   165); // MAX_SPEED  [0- 1024]:
SPIwrite_2byteCmd(0x08,     0); // MIN_SPEED  [0- 8196]:
SPIwrite_2byteCmd(0x15,    40); // FS_SPD     [0- 1024]:
SPIwrite_2byteCmd(0x0D, 10000); // INT_SPEED  [0-16384]:
SPIwrite_2byteCmd(0x18,0x2E80); // CONFIG     [ 0x0000]:
SPIwrite_1byteCmd(0x09,    60); // KVAL_HOLD  [0-  256]:モータ停止時の電圧設定
SPIwrite_1byteCmd(0x0A,   160); // KVAL_RUN   [0-  256]:モータ定速時の電圧設定
SPIwrite_1byteCmd(0x0B,   160); // KVAL_ACC   [0-  256]:モータ加速時の電圧設定
SPIwrite_1byteCmd(0x0C,   160); // KVAL_DEC   [0-  256]:モータ減速時の電圧設定
SPIwrite_1byteCmd(0x0E,   250); // ST_SLP     [0-  256]:
SPIwrite_1byteCmd(0x0F,   250); // FN_SLP_ACC [0-  256]:
SPIwrite_1byteCmd(0x10,    40); // FN_SLP_DEC [0-  256]:
SPIwrite_1byteCmd(0x11,     0); // K_THERM    [0-   16]:
SPIwrite_1byteCmd(0x13,    15); // OCD_TH     [0-   16]:過電流の電流スレショルド
SPIwrite_1byteCmd(0x14,   110); // STALL_TH   [0-  128]:ストールの電流スレショルド
SPIwrite_1byteCmd(0x16,  0x77); // STEP_MODE  [  0x00 ]:励磁モードの設定( 00:Full-step 0x77:1/128microstep
SPIwrite_1byteCmd(0x17,  0x00); // ALARM_EN   [  0x00 ]:
}

// シリアル受信処理
void recv_fun( void )
{
U_8 dummy;
if( rec_cmd[2] == 'r' && rec_cmd[1] == 'u' && rec_cmd[0] == 'n' )
{
SPIwrite_3byteCmd(0x51, 1000); // run命令受信時の処理
}
if( rec_cmd[3] == 's' && rec_cmd[2] == 't' && rec_cmd[1] == 'o' && rec_cmd[0] == 'p' )
{
dummy = SPIMsCom(0xB0); // stop命令受信時の処理
}
}

// 【SPI通信】 1バイト書込み&読込み
U_8  SPIMsCom(U_8  txdat)
{
U_8  rxdat;
PORTD  = 0x00  ; // CS Low
__delay_us(10) ; // 時間待ち
SSPBUF =  txdat; // マスタ送信データ設定,  送受信開始
while(!SSPSTATbits.BF){}; //  SSPIFセット(通信完了)待ち
rxdat  =  SSPBUF; // マスタ受信データ取り出し
PORTD = 0x03; // CS High
return( rxdat );
}

// 【SPI通信】 3~1バイトデータの書込み
void  SPIwrite_3byteCmd(U_8 cmd, U32 data)
{
U_8 dummy;
dummy = SPIMsCom(cmd); // コマンド送信
dummy = SPIMsCom((data>>16)&0xFF); // データ送信(17-24bit)
dummy = SPIMsCom((data>> 8)&0xFF); // データ送信( 8-16bit)
dummy = SPIMsCom((data    )&0xFF); // データ送信( 0- 7bit)
}

void  SPIwrite_2byteCmd(U_8 cmd, U16 data)
{
U_8 dummy;
dummy = SPIMsCom(cmd); // コマンド送信
dummy = SPIMsCom((data>> 8)&0xFF); // データ送信( 8-16bit)
dummy = SPIMsCom((data    )&0xFF); // データ送信( 0- 7bit)
}

void  SPIwrite_1byteCmd(U_8 cmd, U_8 data)
{
U_8 dummy;
dummy = SPIMsCom(cmd); // コマンド送信
dummy = SPIMsCom(data); // データ送信( 0- 7bit)
}

// 【SPI通信】 3~1バイトデータの読込み
U32  SPIread_3byteCmd(U_8 adr, U32 mskbit)
{
U_8 dummy,data[3];
U32 ret_data;
dummy   = SPIMsCom(adr); // アドレス送信
data[2] = SPIMsCom(0); // データ受信(17-24bit) (ダミーデータ送信)
data[1] = SPIMsCom(0); // データ受信( 8-16bit) (ダミーデータ送信)
data[0] = SPIMsCom(0); // データ受信( 0- 7bit) (ダミーデータ送信)
ret_data = (((U32)data[2] << 16)&0x00FF0000) | (((U32)data[1] << 8)&0x0000FF00) | (((U32)data[0])&0x000000FF);
return(ret_data&mskbit);
}

U16  SPIread_2byteCmd(U_8 adr, U16 mskbit)
{
U_8 dummy,data[2];
U16 ret_data;
dummy   = SPIMsCom(adr); // アドレス送信
data[1] = SPIMsCom(0); // データ受信( 8-16bit) (ダミーデータ送信)
data[0] = SPIMsCom(0); // データ受信( 0- 7bit) (ダミーデータ送信)
ret_data = (((U16)data[1] << 8)&0xFF00) | (((U16)data[0])&0x00FF);
return(ret_data&mskbit);
}

U_8  SPIread_1byteCmd(U_8 adr, U_8 mskbit)
{
U_8 dummy;
U_8 ret_data;
dummy   = SPIMsCom(adr); // アドレス送信
ret_data= SPIMsCom(0); // データ受信( 0- 7bit) (ダミーデータ送信)
return(ret_data&mskbit);
}

// 入力処理
void input__fun( void )
{
}

// タスク処理
void task___fun( void )
{
I16 i,d;
U32 tempU32;
// 500msec周期処理(LED点滅や動作確認用)
if(alt500m_ctr >= P_CHECK ) // 10msecタスクをP_CHECKで500msec周期
{
if( bit1.flick_flg == OFF ){ bit1.LED = ON ; bit1.flick_flg = ON ; }
else                       { bit1.LED = OFF; bit1.flick_flg = OFF; }
alt500m_ctr = 0;
// L6470の状態出力
tempU32 =  SPIread_3byteCmd(0x24, 0x000FFFFF);
printf("SPEED:%lu , ",tempU32);
tempU32 =  SPIread_3byteCmd(0x21, 0x000FFFFF);
printf("ABS_POS:%lu\r\n",tempU32);
}
alt500m_ctr ++;
// 受信処理(送信命令の場合は送信を実行)
if( bit1.rec_flg == ON )
{
recv_fun(); // 受信処理
bit1.rec_flg = OFF; // 受信フラグをセット
}
}

// 出力処理
void output_fun( void )
{
RA0 = bit1.LED;
}

// メインの処理
void main()
{
init_fun(); // 初期化処理
while(1)
{
if( bit1.task_exec_flg == ON )
{
input__fun(); // 入力処理
task___fun(); // 演算処理
output_fun(); // 出力処理
bit1.task_exec_flg = OFF; // タスク演算終了確認フラグをOFF(実行済状態へ)
}
}
}

*1:unsigned char)(((_XTAL_FREQ / 16) / BAUDRATE) - 1

*2:unsigned char)(((_XTAL_FREQ / 64) / BAUDRATE) - 1