|
|
View previous topic :: View next topic |
Author |
Message |
Ttelmah
Joined: 11 Mar 2010 Posts: 19540
|
|
Posted: Mon Dec 17, 2012 9:35 am |
|
|
No guarantees.
This was the original code I generated from the application note. However I never used it on this chip, switching to a PIC18, and then having to do a lot of changes (fault with AN3 on the chip, and the CCP behaves differently from a PIC16 one), so this was never tried:
Code: |
//Code based on AN857 for BLDC
#include <16F877.h>
#device adc=16 //Left justify the ADC
#FUSES NOWDT, HS, NOBROWNOUT, NOLVP
#use delay(clock=16MHz)
#define ManThresh (0x3F) //Change to 0xFF for manual mode - 0x3F else - using 44 - almost immediate
#define AutoThresh (0x100-ManThresh)
int8 flags=0;
#bit DriveOnFlag=flags.0
#bit AutoRPM=flags.1
#bit FullOnFlag=flags.4
#bit Tmr0Ovf=flags.5
#bit Tmr0Sync=flags.6
int8 PhaseIndx=6,Drive,RPMIndex;
int8 ADCRPM;
int8 PWMThresh=0;
int8 ADCOffset;
int8 Vsupply;
int8 DeltaV1,DeltaV2;
int16 CCPSave,CCPT2;
int8 RampTimer;
#define AccelDelay 100
#define DecelDelay 10
#bit BEMF1Low=DeltaV1.7
#bit BEMF2Low=DeltaV2.7
#bit ADCneg=ADCOffset.7
enum {RPMSetup,RPMRead,OffsetSetup,OffsetRead,Vsetup,Vidle,Vread,BEMFSetup,BEMFIdle,BEMFRead,BEMF2Idle,BEMF2Read,Inval} STATE=RPMsetup;
#use fast_io(C)
/*
ADC channels are wired as:
AN0 = RPM I/P voltage
AN1 = Offset
AN3 = sense on OP/A BEMF etc..
*/
#define RPM (0)
#define OFFSET (1)
#define BEMF (3)
//Drive word definitions
#define OffMask (0B11010101)
#define Invalid (0B00000000)
#define Phase1 (0B00100001)
#define Phase2 (0B00100100)
#define Phase3 (0B00000110)
#define Phase4 (0B00010010)
#define Phase5 (0B00011000)
#define Phase6 (0B00001001)
//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)
#byte ADCON0=getenv("SFR:ADCON0") //0xFC2 //("SFR:ADCON0")
#byte ADRESH=getenv("SFR:ADRESH") //0xFC4 //("SFR:ADRESH")
#bit GO=ADCON0.1
#byte STAT=getenv("SFR:STATUS")
#bit CARRY=STAT.0
#bit ZERO=STAT.2
#byte TMR0=getenv("SFR:TMR0")
#byte CCP1CON=getenv("SFR:CCP1CON")
#bit SPECIAL_EVENT=CCP1CON.0
#include "T1Tableold.h" //You will need to use the code as shown and build this for your motor
//format is const int16 Timer1Table[256] = {xxx,xxx...};
int save_w;
#locate save_w=0x7f
int save_status;
#locate save_status=0x20
#byte status = 3
#bit zero_flag = status.2
#bit t0if = 0xb.2
#INT_GLOBAL
void timer_0_int(void) {
#asm
//store current state of processor
MOVWF save_w
SWAPF status,W
BCF status,5
BCF status,6
MOVWF save_status
// Save anything else your code may change
// You need to do something with PCLATH if you have GOTOs
// remember to check to see what interrupt fired if using more than one!!
// code for isr
#endasm
Tmr0Ovf=TRUE;
Tmr0Sync=TRUE;
clear_interrupt(INT_TIMER0); //fastest possible int handler - relies on instructions not changing flags etc..
#asm
// restore processor and return from interrupt
SWAPF save_status,W
MOVWF status
SWAPF save_w,F
SWAPF save_w,W
#endasm
}
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); //Changing to start with my current reading
STATE=RPMSetup;
}
void main(void)
{
int8 temp;
output_c(0); //Motor drivers off to start....
set_tris_c(0);
//setup_adc_ports(sAN0|sAN1|sAN3);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(RPM);
setup_spi(FALSE);
setup_timer_0(T0_DIV_2);
enable_interrupts(INT_TIMER0);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
setup_ccp1(CCP_COMPARE_RESET_TIMER);
clear_interrupt(INT_TIMER0);
enable_interrupts(GLOBAL);
do {
if (interrupt_active(INT_CCP1)) commutate();
DriveOnFlag=TRUE;
if (!FullOnFlag) {
temp=(int8)PWMThresh+TMR0;
if (!CARRY) DriveOnFlag=FALSE;
DriveMotor();
}
LockTest();
switch (STATE) {
case RPMSetup:
if (Drive==Phase1) {
output_high(PIN_A4);
read_adc(ADC_START_ONLY);
set_adc_channel(OFFSET); //Offset default
STATE++;
Tmr0Sync=FALSE;
output_low(PIN_A4);
}
break;
case RPMRead:
if (adc_done()) {
ADCRPM=ADRESH;
}
break;
case OffsetSetup: //Wait for Phase2 ADC Go RA3->ADC
if (Drive==Phase2) {
read_adc(ADC_START_ONLY); //Trigger ADC
set_adc_channel(BEMF);
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) {
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
}
break;
case Inval:
STATE=RPMSetup;
//Status=0;
set_adc_channel(RPM);
break;
}
} while(TRUE);
}
void DriveMotor(void) {
if (DriveOnFlag) {
output_c(Drive | 0x80);
}
else {
output_c(Drive & OffMask);
}
}
void LockTest(void) {
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 (ADCRPM<=ManThresh) {
AutoRPM=FALSE;
}
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;
}
//LT20 - else
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;
}
//Showlocked
output_high(PIN_B6);
if(AutoRPM==FALSE) {
//Manual code
RPMIndex=ADCRPM;
}
}
}
|
You'll have to use the spreadsheet in the application note, to build a a T1Tableold.h, to match your motor requirements.
You'll also have to add the instructions to save the registers to int_global (look at ex_glint.c), I'd already started assuming I was going to use the PIC18, which saves the critical registers automatically.
Have done this for you now....
Best Wishes |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Tue Dec 18, 2012 6:20 pm |
|
|
A_L_E_X wrote: | Quote: | Have a look at Microchip AN857, which shows how to have a 877A, handle a BLDC motor, including reading a pot to set the speed. |
I did that but the code is in ASM and i don't know ASM. Is there a version in c ? It doesn't have to be for CCS, just c. |
If you are going to do this sort of intense optimized embedded processor programming in C or any other language you MUST have at least a familiarity with ASM for the chip you are using. Spend some time exploring the instruction set and registers. It will be a good investment. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 18, 2012 7:22 pm |
|
|
If you want to Google for CCS code, then try a search string like this:
Quote: |
18F4431 OR PIC18F4431 BLDC "#use delay"
|
You can also try it with PICs in the same family, such as the 18F2331,
18F2431 and 18F4331. |
|
|
|
|
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
|