📄 avr.cpp
字号:
WriteMemory(Prog.mcu->dirRegs[i], isOutput[i]);
// turn on the pull-ups, and drive the outputs low to start
WriteMemory(Prog.mcu->outputRegs[i], isInput[i]);
}
}
ConfigureTimer1(Prog.cycleTime);
// and now the generated PLC code will follow
BeginningOfCycleAddr = AvrProgWriteP;
// Okay, so many AVRs have a register called TIFR, but the meaning of
// the bits in that register varies from device to device...
int tifrBitForOCF1A;
if(strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega162 40-PDIP")==0) {
tifrBitForOCF1A = 6;
} else {
tifrBitForOCF1A = 4;
}
DWORD now = AvrProgWriteP;
IfBitClear(REG_TIFR, tifrBitForOCF1A);
Instruction(OP_RJMP, now, 0);
SetBit(REG_TIFR, tifrBitForOCF1A);
Instruction(OP_WDR, 0, 0);
}
//-----------------------------------------------------------------------------
// Handle an IF statement. Flow continues to the first instruction generated
// by this function if the condition is true, else it jumps to the given
// address (which is an FwdAddress, so not yet assigned). Called with IntPc
// on the IF statement, returns with IntPc on the END IF.
//-----------------------------------------------------------------------------
static void CompileIfBody(DWORD condFalse)
{
IntPc++;
CompileFromIntermediate();
if(IntCode[IntPc].op == INT_ELSE) {
IntPc++;
DWORD endBlock = AllocFwdAddr();
Instruction(OP_RJMP, endBlock, 0);
FwdAddrIsNow(condFalse);
CompileFromIntermediate();
FwdAddrIsNow(endBlock);
} else {
FwdAddrIsNow(condFalse);
}
if(IntCode[IntPc].op != INT_END_IF) oops();
}
//-----------------------------------------------------------------------------
// Call a subroutine, using either an rcall or an icall depending on what
// the processor supports or requires.
//-----------------------------------------------------------------------------
static void CallSubroutine(DWORD addr)
{
if(Prog.mcu->avrUseIjmp) {
Instruction(OP_LDI, 30, FWD_LO(addr));
Instruction(OP_LDI, 31, FWD_HI(addr));
Instruction(OP_ICALL, 0, 0);
} else {
Instruction(OP_RCALL, addr, 0);
}
}
//-----------------------------------------------------------------------------
// Compile the intermediate code to AVR native code.
//-----------------------------------------------------------------------------
static void CompileFromIntermediate(void)
{
DWORD addr, addr2;
int bit, bit2;
DWORD addrl, addrh;
DWORD addrl2, addrh2;
for(; IntPc < IntCodeLen; IntPc++) {
IntOp *a = &IntCode[IntPc];
switch(a->op) {
case INT_SET_BIT:
MemForSingleBit(a->name1, FALSE, &addr, &bit);
SetBit(addr, bit);
break;
case INT_CLEAR_BIT:
MemForSingleBit(a->name1, FALSE, &addr, &bit);
ClearBit(addr, bit);
break;
case INT_COPY_BIT_TO_BIT:
MemForSingleBit(a->name1, FALSE, &addr, &bit);
MemForSingleBit(a->name2, FALSE, &addr2, &bit2);
CopyBit(addr, bit, addr2, bit2);
break;
case INT_SET_VARIABLE_TO_LITERAL:
MemForVariable(a->name1, &addrl, &addrh);
WriteMemory(addrl, a->literal & 0xff);
WriteMemory(addrh, a->literal >> 8);
break;
case INT_INCREMENT_VARIABLE: {
MemForVariable(a->name1, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrh);
Instruction(OP_LD_X, 17, 0);
// increment
Instruction(OP_INC, 16, 0);
DWORD noCarry = AllocFwdAddr();
Instruction(OP_BRNE, noCarry, 0);
Instruction(OP_INC, 17, 0);
FwdAddrIsNow(noCarry);
// X is still addrh
Instruction(OP_ST_X, 17, 0);
LoadXAddr(addrl);
Instruction(OP_ST_X, 16, 0);
break;
}
case INT_IF_BIT_SET: {
DWORD condFalse = AllocFwdAddr();
MemForSingleBit(a->name1, TRUE, &addr, &bit);
IfBitClear(addr, bit);
Instruction(OP_RJMP, condFalse, 0);
CompileIfBody(condFalse);
break;
}
case INT_IF_BIT_CLEAR: {
DWORD condFalse = AllocFwdAddr();
MemForSingleBit(a->name1, TRUE, &addr, &bit);
IfBitSet(addr, bit);
Instruction(OP_RJMP, condFalse, 0);
CompileIfBody(condFalse);
break;
}
case INT_IF_VARIABLE_LES_LITERAL: {
DWORD notTrue = AllocFwdAddr();
MemForVariable(a->name1, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrh);
Instruction(OP_LD_X, 17, 0);
Instruction(OP_LDI, 18, (a->literal & 0xff));
Instruction(OP_LDI, 19, (a->literal >> 8));
Instruction(OP_CP, 16, 18);
Instruction(OP_CPC, 17, 19);
Instruction(OP_BRGE, notTrue, 0);
CompileIfBody(notTrue);
break;
}
case INT_IF_VARIABLE_GRT_VARIABLE:
case INT_IF_VARIABLE_EQUALS_VARIABLE: {
DWORD notTrue = AllocFwdAddr();
MemForVariable(a->name1, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrh);
Instruction(OP_LD_X, 17, 0);
MemForVariable(a->name2, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_LD_X, 18, 0);
LoadXAddr(addrh);
Instruction(OP_LD_X, 19, 0);
if(a->op == INT_IF_VARIABLE_EQUALS_VARIABLE) {
Instruction(OP_CP, 16, 18);
Instruction(OP_CPC, 17, 19);
Instruction(OP_BRNE, notTrue, 0);
} else if(a->op == INT_IF_VARIABLE_GRT_VARIABLE) {
DWORD isTrue = AllocFwdAddr();
// true if op1 > op2
// false if op1 >= op2
Instruction(OP_CP, 18, 16);
Instruction(OP_CPC, 19, 17);
Instruction(OP_BRGE, notTrue, 0);
} else oops();
CompileIfBody(notTrue);
break;
}
case INT_SET_VARIABLE_TO_VARIABLE:
MemForVariable(a->name1, &addrl, &addrh);
MemForVariable(a->name2, &addrl2, &addrh2);
LoadXAddr(addrl2);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrl);
Instruction(OP_ST_X, 16, 0);
LoadXAddr(addrh2);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrh);
Instruction(OP_ST_X, 16, 0);
break;
case INT_SET_VARIABLE_DIVIDE:
// Do this one separately since the divide routine uses
// slightly different in/out registers and I don't feel like
// modifying it.
MemForVariable(a->name2, &addrl, &addrh);
MemForVariable(a->name3, &addrl2, &addrh2);
LoadXAddr(addrl2);
Instruction(OP_LD_X, 18, 0);
LoadXAddr(addrh2);
Instruction(OP_LD_X, 19, 0);
LoadXAddr(addrl);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrh);
Instruction(OP_LD_X, 17, 0);
CallSubroutine(DivideAddress);
DivideUsed = TRUE;
MemForVariable(a->name1, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_ST_X, 16, 0);
LoadXAddr(addrh);
Instruction(OP_ST_X, 17, 0);
break;
case INT_SET_VARIABLE_ADD:
case INT_SET_VARIABLE_SUBTRACT:
case INT_SET_VARIABLE_MULTIPLY:
MemForVariable(a->name2, &addrl, &addrh);
MemForVariable(a->name3, &addrl2, &addrh2);
LoadXAddr(addrl);
Instruction(OP_LD_X, 18, 0);
LoadXAddr(addrh);
Instruction(OP_LD_X, 19, 0);
LoadXAddr(addrl2);
Instruction(OP_LD_X, 16, 0);
LoadXAddr(addrh2);
Instruction(OP_LD_X, 17, 0);
if(a->op == INT_SET_VARIABLE_ADD) {
Instruction(OP_ADD, 18, 16);
Instruction(OP_ADC, 19, 17);
} else if(a->op == INT_SET_VARIABLE_SUBTRACT) {
Instruction(OP_SUB, 18, 16);
Instruction(OP_SBC, 19, 17);
} else if(a->op == INT_SET_VARIABLE_MULTIPLY) {
CallSubroutine(MultiplyAddress);
MultiplyUsed = TRUE;
} else oops();
MemForVariable(a->name1, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_ST_X, 18, 0);
LoadXAddr(addrh);
Instruction(OP_ST_X, 19, 0);
break;
case INT_SET_PWM: {
int target = atoi(a->name2);
// PWM frequency is
// target = xtal/(256*prescale)
// so not a lot of room for accurate frequency here
int prescale;
int bestPrescale;
int bestError = INT_MAX;
int bestFreq;
for(prescale = 1;;) {
int freq = (Prog.mcuClock + prescale*128)/(prescale*256);
int err = abs(freq - target);
if(err < bestError) {
bestError = err;
bestPrescale = prescale;
bestFreq = freq;
}
if(prescale == 1) {
prescale = 8;
} else if(prescale == 8) {
prescale = 64;
} else if(prescale == 64) {
prescale = 256;
} else if(prescale == 256) {
prescale = 1024;
} else {
break;
}
}
if(((double)bestError)/target > 0.05) {
Error(_("Target frequency %d Hz, closest achievable is "
"%d Hz (warning, >5%% error)."), target, bestFreq);
}
DivideUsed = TRUE; MultiplyUsed = TRUE;
MemForVariable(a->name1, &addrl, &addrh);
LoadXAddr(addrl);
Instruction(OP_LD_X, 16, 0);
Instruction(OP_LDI, 17, 0);
Instruction(OP_LDI, 19, 0);
Instruction(OP_LDI, 18, 255);
CallSubroutine(MultiplyAddress);
Instruction(OP_MOV, 17, 19);
Instruction(OP_MOV, 16, 18);
Instruction(OP_LDI, 19, 0);
Instruction(OP_LDI, 18, 100);
CallSubroutine(DivideAddress);
LoadXAddr(REG_OCR2);
Instruction(OP_ST_X, 16, 0);
// Setup only happens once
MemForSingleBit("$pwm_init", FALSE, &addr, &bit);
DWORD skip = AllocFwdAddr();
IfBitSet(addr, bit);
Instruction(OP_RJMP, skip, 0);
SetBit(addr, bit);
BYTE cs;
switch(bestPrescale) {
case 1: cs = 1; break;
case 8: cs = 2; break;
case 64: cs = 3; break;
case 256: cs = 4; break;
case 1024: cs = 5; break;
default: oops(); break;
}
// fast PWM mode, non-inverted operation, given prescale
WriteMemory(REG_TCCR2, (1 << 6) | (1 << 3) | (1 << 5) | cs);
FwdAddrIsNow(skip);
break;
}
case INT_EEPROM_BUSY_CHECK: {
MemForSingleBit(a->name1, FALSE, &addr, &bit);
DWORD isBusy = AllocFwdAddr();
DWORD done = AllocFwdAddr();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -