Reputation: 21
I'm looking to make a very short pulse after a rising edge signal input.
The hard part here is that I would like to control (to high resolution) the timing of the delay before my pulse, and the duration of my pulse. I can easily control this by just stringing together nops by myself, hard coding delays, but I'm not sure how to do it for some arbitrary delay, with the same level of accuracy.
After a lot of headaches chasing down timers, and then eventually realizing I am ultimately limited by the interrupt routine entry/exit time, I am now settling at trying to control my delay via nops.
I had assumed this C switch statement would be what I wanted (after compiling, hoping it would become efficient and just change the program counter to the right spot), but it produces some very odd behavior...
switch(delayTime){
case 10:
__asm__ __volatile__("nop");
case 9:
__asm__ __volatile__("nop");
case 8:
__asm__ __volatile__("nop");
case 7:
__asm__ __volatile__("nop");
case 6:
__asm__ __volatile__("nop");
case 5:
__asm__ __volatile__("nop");
case 4:
__asm__ __volatile__("nop");
case 3:
__asm__ __volatile__("nop");
case 2:
__asm__ __volatile__("nop");
case 1:
__asm__ __volatile__("nop");
}
PORTD = 0x10;
...
Ideally, I would like to essentially run through some code that would compile into this: (it's some weird pseudocode of C and assembly, still not sure how to do some of it in assembly)
0x005 Reg1 = 0xFF-val1 %(where somehow 0xFF is known? / found out?)
0x006 Reg2 =0x1FF-val2
0x007 IJMP Reg1
0x008 NOP
0x009 NOP
0x00A NOP
...
0x0FF MOV 0x40, PORTD % assign the value 0x40 to the static variable "PORTD"
0x100 IJMP Reg2
0x101 NOP
0x102 NOP
0x103 NOP
0x104 NOP
...
0x1FF MOV 0x00, PORTD % assign the value 0x00 to the static variable "PORTD"
I'm just overall not sure how to find the memory location for the code after/during run time so that the "0xFF" and "0x1FF" aspects of this program are not really so bad (it seems like it's super dangerous to just, get the assembly of the code, and then hard code that in... I'd rather not do that). Also, while it's easy to just flood it with the 200+ nops, how to get the IJMP cmd to behave the way I want it to? (I honestly don't even know if that's the command I want)..
I guess in general I'm looking for some assembly command (that I can't seem to find) that allows me to "add N to Program Counter" and I can just make sure that that command is run in assembly with at least N+1 commands of assembly ahead of it, hardcoded in.
As a side note, all of this is executing inside of an interrupt routine, so I don't feel so bad about playing around with the PC... Also, I know is kinda bad blocking for up to 500 operations, but for the task at hand, timing is more important than how badly it blocks as a routine.
Upvotes: 2
Views: 3309
Reputation: 34829
I'm not familiar with the AVR instruction set, but the general idea is to use the CALL
instruction to put the program counter (PC) on the stack. Then use POP
to move the PC to the Z register. Then you can ADD
some number to the Z register, and use IJMP
to jump to the resulting address.
So something along these lines
delay: call delay1 ; push the PC onto the stack
delay1: pop r30 ; pop the PC into the Z registers
pop r31
add r30,r0 ; add some amount to the PC value
addc r31,r1
ijmp ; use IJMP to jump to the resulting address
nop
nop
nop
...
Random thoughts:
delay1
since call
is
going to push the address of delay1
onto the stack. In other words,
the minimum amount that needs to be added is 6, since that's the
number of instructions from delay1
to the first nop
.ijmp
. You should increase r1/r0 (reduce the number of
nops) accordingly.Like I said, I'm no expert on the AVR instruction set, so you should take this as a general suggestion, and be prepared to spend some time working out the particulars. Good luck!
Upvotes: 1