 |
 |
| View previous topic :: View next topic |
| Author |
Message |
Ttelmah
Joined: 11 Mar 2010 Posts: 20061
|
|
Posted: Tue Jan 26, 2016 3:43 am |
|
|
As a 'possibly interesting', I have dug out my translation of AN857 to CCS.
This was working, so makes a starting point for such a motor. However this is designed to give speed control from an analog control input, not 'position' control, so will need modification. Also the data table for motor rate control is dependant on the motor/drive being used (the AN says how to generate this), so the one attached (which was for a 750W motor at 36V), will not be right for anything else....
To reverse direction, one stops, and then walks through the phase table in the opposite direction.
//The translation of the AN
| Code: |
#include <18F2620.h>
#device adc=16 //Left justify the ADC
#FUSES NOWDT, WDT128, INTRC_IO, NOFCMEN, NOIESO, NOBROWNOUT, NOMCLR, NOLVP, NOXINST
#use delay(clock=32MHz)
//Code based on AN857 for BLDC - for notes, look at the sensorless code in this AN
//Variables directly from AN
int8 AccelDelay=100;
int8 DecelDelay=10;
#define ManThresh (0x3F) //Change to 0xFF for manual mode - 0x3F else
#define AutoThresh (0x100-ManThresh)
//Again from AN
enum {RPMSetup,RPMRead,OffsetSetup,OffsetRead,Vsetup,Vidle,Vread,BEMFSetup,BEMFIdle,BEMFRead,BEMF2Idle,BEMF2Read,Inval} STATE=RPMsetup;
int8 PWMThresh=0;
int8 PhaseIndx=6,Drive,RPMIndex;
int8 ADCRPM;
int8 ADCOffset;
int8 flags=0;
int8 Vsupply;
int8 DeltaV1,DeltaV2;
int16 CCPSave,CCPT2;
int8 RampTimer;
#bit DriveOnFlag=flags.0
#bit AutoRPM=flags.1
#bit FullOnFlag=flags.4
#bit Tmr0Ovf=flags.5
#bit Tmr0Sync=flags.6
int1 change=FALSE;
#bit BEMF1Low=DeltaV1.7
#bit BEMF2Low=DeltaV2.7
#bit ADCneg=ADCOffset.7
#use fast_io(C) //for fastest output of drive bits
/*
ADC channels are wired as:
AN0 = RPM I/P voltage
AN1 = Offset I/P voltage
AN2 = BEMF sensor
*/
#define BEMF (2)
#define OFFSET (1)
#define RPM (0)
//Drive word definitions
#define OffMask (0B10010101)
#define Invalid (0B00000000)
#define Phase1 (0B01000001)
#define Phase2 (0B01000100)
#define Phase3 (0B00000110)
#define Phase4 (0B00010010)
#define Phase5 (0B00011000)
#define Phase6 (0B00001001)
//Again changed from example RC5, does not work as normal I/O on the PIC18 when special
//event triggers are selected.
//So pins RC6 & RC7 used for third output pair.
//ADC switching definitions
//Each assumes the ADC is in a defined previous state, and changes it to the next
#define ADC0to1 (0B00001000)
#define ADC1to3 (0B00010000)
#define ADC3to0 (0B00011000) //Not used.
#byte ADCON0=getenv("SFR:ADCON0") //0xFC2
#byte ADRESH=getenv("SFR:ADRESH") //0xFC4
#bit GO=ADCON0.1
#byte STAT=getenv("SFR:STATUS")
#bit CARRY=STAT.0
#bit ZERO=STAT.2
#byte TMR0=getenv("SFR:TMR0L")
#byte CCP1CON=getenv("SFR:CCP1CON")
#bit SPECIAL_EVENT=CCP1CON.0
//Adding diagnostics
//Pin B6 - high = locked
//B7 - high = fast
//A4 - pulse on phase1
//A5 pulsed on two BEMF readings
#define POWER_OFF (PIN_B4) //If this goes true, turn off drive (fail safe).
#include "T1Table.h" //This is the constant timing table calculated as in the AN
#INT_GLOBAL
void timer_0_int(void) {
Tmr0Ovf=TRUE;
Tmr0Sync=TRUE;
clear_interrupt(INT_TIMER0); //fastest possible int handler - relies on instructions not changing flags etc..
}
const int8 OnTable[]={Invalid,Phase6,Phase5,Phase4,Phase3,Phase2,Phase1,Invalid};
void DriveMotor(void);
void LockTest(void);
void commutate(void) {
//Commutation triggered by CCP1IF
if (SPECIAL_EVENT) {
//Here special event trigger is set
clear_interrupt(INT_CCP1);
if (--PhaseIndx==0) PhaseIndx=6; //Handle 'bottom of table'
Drive=OnTable[PhaseIndx];
DriveMotor();
}
}
void DriveOff(void) {
set_adc_channel(RPM); //Start with RPM required
STATE=RPMSetup;
}
void main()
{
int8 temp;
output_c(0); //Motor drivers off to start....
set_tris_c(0);
setup_adc_ports(AN0_TO_AN2);
setup_adc(ADC_CLOCK_DIV_32|ADC_TAD_MUL_0); //As in note
set_adc_channel(RPM); //select AN0
delay_us(8);
read_adc(ADC_START_ONLY);
setup_spi(FALSE);
setup_comparator(NC_NC_NC_NC);
setup_timer_0(T0_DIV_2|T0_8_BIT);
enable_interrupts(INT_TIMER0);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
CCP_1=0xFFFF;
setup_ccp1(CCP_OFF);
CCP1CON=0xB; //from AN
clear_interrupt(INT_TIMER0);
enable_interrupts(GLOBAL);
//Now main loop
do {
if (interrupt_active(INT_CCP1)) commutate();
DriveOnFlag=TRUE;
if (!FullOnFlag) {
temp=(int8)PWMThresh+TMR0;
if (!CARRY) DriveOnFlag=FALSE;
DriveMotor();
}
LockTest();
switch (STATE) {
//These are now the states from the AN
case RPMSetup:
if (Drive==Phase1) {
output_high(PIN_A4);
read_adc(ADC_START_ONLY);
set_adc_channel(OFFSET); //Next channel - note change while reading is being done
STATE++;
Tmr0Sync=FALSE;
}
break;
case RPMRead:
if (adc_done()){
//Conversion has finished
ADCRPM=ADRESH; //fastest 8bit result from ADC
STATE++;
}
break;
case OffsetSetup: //Wait for Phase2 ADC Go RA3->ADC
if (Drive==Phase2) {
read_adc(ADC_START_ONLY); //Trigger ADC
set_adc_channel(BEMF); //BEMF channel
STATE++;
}
break;
case OffsetRead:
//Wait for ADC Read ADC->ADC Offset
if (adc_done()){
ADCOffset=ADRESH ^ 0x80; //Generate +/- offset
PWMThresh=ADCRPM+ADCOffset;
if (!bit_test(ADCOffset,7)) { //Offset is +ve
if (CARRY) PWMThresh=0xFF;
}
else {
if(!CARRY) PWMThresh=0;
}
if (ZERO) {
DriveOff();
break;
}
FullOnFlag=FALSE;
if (PWMThresh>0xFD) FullOnFlag=TRUE; //Fixed full on threshold
STATE++;
}
break;
case Vsetup:
//wait for phase4
if (Drive==Phase4) {
//Set Timer routine
CCP_1=Timer1table[RPMIndex]; //Table from spreadsheet
STATE++;
}
break;
case Vidle:
//Wait for Drive ON, wait Tacq, set ADC go
if (DriveOnFlag) {
delay_us(8); //Tacq
read_adc(ADC_START_ONLY);
STATE++;
}
break;
case Vread:
if(adc_done()) {
//ADC has finished
Vsupply=ADRESH;
STATE++;
Tmr0Sync=FALSE;
}
break;
case BEMFSetup:
if(Drive==Phase5) {
if (Tmr0Sync) {
if (bit_test(PWMThresh,7)!=0) {
if (DriveOnFlag==FALSE) break;
}
//BEMFS1
SPECIAL_EVENT=FALSE; //Turn off event on compare
CCPSave=CCPT2=CCP_1; //Save two copies of CCP
CCPT2/=2; //1/2 Phase time
CCP_1=CCPT2/2; //1/4 phase time into CCP_1
STATE++;
}
}
break;
case BEMFIdle:
if (interrupt_active(INT_CCP1)) {
DriveOnFlag=TRUE;
DriveMotor();
Delay_us(8);
read_adc(ADC_START_ONLY);
CCP_1+=CCPT2; //add 1/2 phase time to give 3/4 phase time
clear_interrupt(INT_CCP1);
STATE++;
}
break;
case BEMFRead:
if (adc_done()) {
DeltaV1=ADRESH-(Vsupply/2);
STATE++;
}
break;
case BEMF2Idle:
if (interrupt_active(INT_CCP1)) {
DriveOnFlag=TRUE;
DriveMotor();
delay_us(8);
read_adc(ADC_START_ONLY);
set_adc_channel(RPM);
CCP_1=CCPSave; //Full time
clear_interrupt(INT_CCP1);
SPECIAL_EVENT=TRUE; //enable event on compare
STATE++;
}
break;
case BEMF2Read:
if (adc_done()) {
DeltaV2=ADRESH-(Vsupply/2);
STATE=RPMSetup; //Changing to start with current reading
}
change=TRUE;
break;
case Inval:
STATE=RPMSetup;
//Status=0;
set_adc_channel(RPM);
break;
}
} while(TRUE);
}
void DriveMotor(void) {
if (DriveOnFlag & (input(POWER_OFF)==FALSE)) { //Turning off drive if input goes TRUE
output_c(Drive | 0x80);
}
else {
output_c(Drive & OffMask);
}
}
void LockTest(void) {
change=FALSE;
if(Tmr0Ovf==TRUE) {
if (bit_test(PWMThresh,7)){
//On longer than off
if (DriveOnFlag==FALSE)
return;
}
//Here drive is either on, and on>off, or drive off, and off>on - LT05
Tmr0Ovf=FALSE;
if (--RampTimer!=0)
return;
AutoRPM=TRUE;
if (BEMF1Low) {
//LT10
output_low(PIN_B6);
output_high(PIN_B7); //ahead of lock
RampTimer=AccelDelay;
if (AutoRPM==FALSE) {
//Manual code
RPMIndex=ADCRPM;
return;
}
if (RPMIndex==0xFF) {
return;
}
RPMIndex++;
return;
}
RampTimer=DecelDelay;
if (!BEMF2Low) {
output_low(pin_B6);
output_low(PIN_B7); //behind lock
if (AutoRPM==FALSE) {
//Manual code again
RPMIndex=ADCRPM;
return;
}
if (RPMIndex>0) --RPMIndex;
return;
}
output_high(PIN_B6);
if(AutoRPM==FALSE) {
//Manual code
RPMIndex=ADCRPM;
}
}
}
|
The motor/timer control look up table
| Code: |
const int16 Timer1Table[256] = {
8568,
8539,
8511,
8482,
8453,
8425,
8396,
8367,
8338,
8310,
8281,
8252,
8224,
8195,
8166,
8138,
8109,
8080,
8051,
8023,
7994,
7965,
7937,
7908,
7879,
7851,
7822,
7793,
7764,
7736,
7707,
7678,
7650,
7621,
7592,
7564,
7535,
7506,
7477,
7449,
7420,
7391,
7363,
7334,
7305,
7277,
7248,
7219,
7190,
7162,
7133,
7104,
7076,
7047,
7018,
6990,
6961,
6932,
6903,
6875,
6846,
6817,
6789,
6760,
6731,
6703,
6674,
6645,
6616,
6588,
6559,
6530,
6502,
6473,
6444,
6416,
6387,
6358,
6329,
6301,
6272,
6243,
6215,
6186,
6157,
6129,
6100,
6071,
6042,
6014,
5985,
5956,
5928,
5899,
5870,
5842,
5813,
5784,
5755,
5727,
5698,
5669,
5641,
5612,
5583,
5555,
5526,
5497,
5468,
5440,
5411,
5382,
5354,
5325,
5296,
5268,
5239,
5210,
5181,
5153,
5124,
5095,
5067,
5038,
5009,
4981,
4952,
4923,
4894,
4866,
4837,
4808,
4780,
4751,
4722,
4694,
4665,
4636,
4607,
4579,
4550,
4521,
4493,
4464,
4435,
4407,
4378,
4349,
4320,
4292,
4263,
4234,
4206,
4177,
4148,
4120,
4091,
4062,
4033,
4005,
3976,
3947,
3919,
3890,
3861,
3833,
3804,
3775,
3746,
3718,
3689,
3660,
3632,
3603,
3574,
3546,
3517,
3488,
3459,
3431,
3402,
3373,
3345,
3316,
3287,
3259,
3230,
3201,
3172,
3144,
3115,
3086,
3058,
3029,
3000,
2972,
2943,
2914,
2885,
2857,
2828,
2799,
2771,
2742,
2713,
2685,
2656,
2627,
2598,
2570,
2541,
2512,
2484,
2455,
2426,
2398,
2369,
2340,
2311,
2283,
2254,
2225,
2197,
2168,
2139,
2111,
2082,
2053,
2024,
1996,
1967,
1938,
1910,
1881,
1852,
1824,
1795,
1766,
1737,
1709,
1680,
1651,
1623,
1594,
1565,
1537,
1508,
1479,
1450,
1422,
1393,
1364,
1336,
1307,
1278,
1250
};
|
This will need to be recalculated for any other motor. |
|
 |
mahdi70
Joined: 05 Jan 2016 Posts: 44
|
|
Posted: Tue Jan 26, 2016 4:20 am |
|
|
| Ttelmah wrote: | The first thing is that you don't want three phase the way you show it.....
BLDC motors like this have forward and reverse drive on each coil. The sequence is:
| Code: |
Phase coil A coil B coil C
1 + - 0
2 0 - +
3 - 0 +
4 - + 0
5 0 + -
6 + 0 -
|
If fact for smooth motion, the '+' energisation (for example), wants to be a synthesised sinusoidal waveform, so a much higher frequency PWM, varying from almost nothing to full power and then back down to almost nothing over the cycle. When the motor is stationary, the current PWM ratio on the correct pins has to continue. Two coils active at any one time.
You need six FET drivers, three energising the coils in a positive direction, and three in a -ve direction. A total of six control pins on the PIC.
For the slow speeds that the gimbal will actually rotate, you may well be able to run without BEMF sensing, and instead treat all motions as if they are initial 'start' operations. AN857, does show how to generate these waveforms from just about any PIC. |
tnx...this is my speed control that i design it and install on my quad and work properly...
above speed control work with bemf system...in each rotor station , if bemf sense then goto to next station if not all switch off and show error....the bemf system cant sense speed under 200 rpm...you should rotate motor without bemf....Now the question comes ,Where to find out the rotor rotate properly until goto to next station?
this is example code:
| Code: | while (1){
station(1);//phase a=+ ... b=- ... c=float
delay_ms(10);
station(2);//phase a=+ ... b=float ... c=-
delay_ms(10);
station(3);//phase a=float ... b=+ ... c=-
delay_ms(10);
station(4);//phase a=- ... b=+ ... c=float
delay_ms(10);
station(5);//phase a=- ... b=float ... c=+
delay_ms(10);
station(6);//phase a=float ... b=- ... c=+
delay_ms(10);} |
for example i want to rotate motor with 1 degree per second....how to do it? this is my problem
sorry for poor english
tnx |
|
 |
mahdi70
Joined: 05 Jan 2016 Posts: 44
|
|
Posted: Tue Jan 26, 2016 4:32 am |
|
|
| Ttelmah wrote: | As a 'possibly interesting', I have dug out my translation of AN857 to CCS.
|
Very very tnx but It is a little hard to understand for me. |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|