adding pi zero blinker05 example
This commit is contained in:
70
pizero/blinker05/Makefile
Normal file
70
pizero/blinker05/Makefile
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
ARMGNU ?= arm-none-eabi
|
||||
|
||||
AOPS = --warn --fatal-warnings
|
||||
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding
|
||||
|
||||
|
||||
|
||||
gcc : blinker05.hex blinker05.bin
|
||||
|
||||
all : gcc clang
|
||||
|
||||
clean :
|
||||
rm -f *.o
|
||||
rm -f *.bin
|
||||
rm -f *.hex
|
||||
rm -f *.elf
|
||||
rm -f *.list
|
||||
rm -f *.img
|
||||
rm -f *.bc
|
||||
rm -f *.clang.opt.s
|
||||
|
||||
vectors.o : vectors.s
|
||||
$(ARMGNU)-as vectors.s -o vectors.o
|
||||
|
||||
blinker05.o : blinker05.c
|
||||
$(ARMGNU)-gcc $(COPS) -c blinker05.c -o blinker05.o
|
||||
|
||||
blinker05.elf : memmap vectors.o blinker05.o
|
||||
$(ARMGNU)-ld vectors.o blinker05.o -T memmap -o blinker05.elf
|
||||
$(ARMGNU)-objdump -D blinker05.elf > blinker05.list
|
||||
|
||||
blinker05.bin : blinker05.elf
|
||||
$(ARMGNU)-objcopy blinker05.elf -O binary blinker05.bin
|
||||
|
||||
blinker05.hex : blinker05.elf
|
||||
$(ARMGNU)-objcopy blinker05.elf -O ihex blinker05.hex
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LOPS = -Wall -m32 -emit-llvm
|
||||
LLCOPS = -march=arm -mcpu=arm1176jzf-s
|
||||
LLCOPS0 = -march=arm
|
||||
LLCOPS1 = -march=arm -mcpu=arm1176jzf-s
|
||||
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
|
||||
OOPS = -std-compile-opts
|
||||
|
||||
clang : blinker05.clang.hex blinker05.clang.bin
|
||||
|
||||
|
||||
blinker05.clang.bc : blinker05.c
|
||||
clang $(LOPS) -c blinker05.c -o blinker05.clang.bc
|
||||
|
||||
blinker05.clang.opt.elf : memmap vectors.o blinker05.clang.bc
|
||||
opt $(OOPS) blinker05.clang.bc -o blinker05.clang.opt.bc
|
||||
llc $(LLCOPS) blinker05.clang.opt.bc -o blinker05.clang.opt.s
|
||||
$(ARMGNU)-as blinker05.clang.opt.s -o blinker05.clang.opt.o
|
||||
$(ARMGNU)-ld -o blinker05.clang.opt.elf -T memmap vectors.o blinker05.clang.opt.o
|
||||
$(ARMGNU)-objdump -D blinker05.clang.opt.elf > blinker05.clang.opt.list
|
||||
|
||||
blinker05.clang.hex : blinker05.clang.opt.elf
|
||||
$(ARMGNU)-objcopy blinker05.clang.opt.elf blinker05.clang.hex -O ihex
|
||||
|
||||
blinker05.clang.bin : blinker05.clang.opt.elf
|
||||
$(ARMGNU)-objcopy blinker05.clang.opt.elf blinker05.clang.bin -O binary
|
||||
|
||||
|
||||
115
pizero/blinker05/README
Normal file
115
pizero/blinker05/README
Normal file
@@ -0,0 +1,115 @@
|
||||
See the top level README for information on where to find documentation
|
||||
for the raspberry pi and the ARM processor inside. Also find information
|
||||
on how to load and run these programs.
|
||||
|
||||
This example is for the pi zero, see other directories for other flavors
|
||||
of raspberry pi.
|
||||
|
||||
This is derived from blinker04.
|
||||
|
||||
I dont understand why but way too many blinker examples use interrupts.
|
||||
In short only use interrupts if you have to, often you dont have to.
|
||||
If you feel you have to this example shows you a little about enabling
|
||||
and handling interrupts on a raspberry pi.
|
||||
|
||||
Interrupts are an advanced programming topic, no matter how simple
|
||||
this example makes it appear, there is a long list of things that can
|
||||
and will go wrong. I definitely didnt get this working on the first
|
||||
or second or third try. It took many. Fortunately on this system
|
||||
we have a few layers we can play with before actually interrupting
|
||||
the processor. This is both a blessing and a curse, some systems
|
||||
have a number of layers, not all easy to find in the docs if in the
|
||||
docs and all layers have to have the right enables, etc to route the
|
||||
interrupt from the peripheral to processor. blinker04 showed a timer
|
||||
with a raw interrupt status register, basically the interrupt signal
|
||||
near its source. The interrupt enable bit on the timer is the first
|
||||
layer you have to enable the signal through. This example kills three
|
||||
birds with one stone. Instead of three examples I enable one layer at
|
||||
a time and show a few blinks with each layer before completely
|
||||
connecting the interrupt to the processor.
|
||||
|
||||
The first problem we have with the raspberry pi is where the binary
|
||||
is loaded. I have had the most success by not mucking with settings
|
||||
which means that our program is loaded at address 0x8000. For this
|
||||
ARM core to service an interrupt we need to have our exception table at
|
||||
address 0x0000 (newer cores we can move that address).
|
||||
|
||||
If you have been reading your ARM documents as you should by this point
|
||||
you will know that for an interrupt on this processor it executes the
|
||||
instruction at address 0x0001C. Also the interrupt mode has a different
|
||||
r13 or stack pointer than supervisor mode. So if we plan to use the
|
||||
stack at all in the interrupt handler we need to prepare that stack
|
||||
pointer before any interrupts come along.
|
||||
|
||||
Before we get to the stack pointer, looking at vectors.s. We know that
|
||||
0x8000 is our entry point where _start will be. We know that is not the
|
||||
exception table but I have made one there anyway, why? There are a
|
||||
number of ways to do this, this way we get the assembler/linker to do
|
||||
some of the work for us. Using the ldr pc,label instruction I am
|
||||
letting the assembler do the work of creating the machine code for a
|
||||
relative load.
|
||||
|
||||
Address 0x8000 loads into the program counter (same as doing a branch if
|
||||
you are not switching modes) the address contained at address 0x8020
|
||||
which is filled in (by the assembler) the address for the reset label.
|
||||
This repeats one for one for 8 handlers 0x8004 gets address in 0x8024,
|
||||
etc. I happen to know and you can look up the fact that ldr pc,label
|
||||
when used this way uses relative addressing
|
||||
|
||||
8000: e59ff018 ldr pc, [pc, #24] ; 8020 <reset_handler>
|
||||
...
|
||||
00008020 <reset_handler>:
|
||||
8020: 00008040 andeq r8, r0, r0, asr #32
|
||||
|
||||
The machine instruction 0xe59ff018 when placed at address 0x1000 will
|
||||
read from address 0x1000+0x20, basically pc+0x20 (the disassembly is
|
||||
misleading). So if I were to copy the instruction at 0x8000 to 0x0000
|
||||
and the address at 0x8020 to 0x0020, then it still works, a reset
|
||||
exception if we could create one would read 0x8040 in this case and put
|
||||
that in the pc, causing a jump to 0x8040. Same is true for all 8
|
||||
instructions and all 8 addresses. So the first thing the code does
|
||||
after reset is copy those 16 words from 0x8000 to 0x0000, we didnt have
|
||||
to figure out addresses for functions and didnt have to generate machine
|
||||
code this approach let the assembler do the work.
|
||||
|
||||
The next thing the reset code does is set up the stack pointer, not
|
||||
much different than the other example programs except in this case
|
||||
we need to setup both the supervisor mode, which we normally run
|
||||
these examples in, and interrupt mode. The modes determine which
|
||||
registers are used, the stack pointer being one that has a different
|
||||
register for each mode. The trick is to use the mrs instruction to
|
||||
change some of the bits in the program status register (psr) the bits
|
||||
in particular we care about are the mode bits. Change the mode to
|
||||
interrupt, set the interrupt stack pointer, change the mode back to
|
||||
supervisor and change the stack pointer, then continue by calling the
|
||||
entry C function, notmain().
|
||||
|
||||
Just like blinker04, blinker05 configures the gpio pin for driving the
|
||||
led and sets up the timer. Different from blinker04, interrupts are
|
||||
enabled in the control register (bit 5). Using the assumption that this
|
||||
program was run just after reset, either as kernel.img on the sd card
|
||||
or using one of my bootloaders that doesnt mess with enabling interrupts,
|
||||
etc. The interrupt will leave the timer but get stopped at the next layer.
|
||||
Since it is leaving the timer we will be able to see it in the masked
|
||||
interrupt status register. So the first blinks, once state change
|
||||
per second, are based on polling the MIS register.
|
||||
|
||||
The next layer is between the timer and the ARM. Each chip vendor and
|
||||
model can vary wildly as to how many layers and how to use them. In
|
||||
this case we have a relatively simple interrupt enable. By setting
|
||||
a bit in the irq enable register we are allowing the arm timer interrupt
|
||||
to pass through to the ARM core. Because our early cpsr registers
|
||||
manipulation (to set up the interrupt stack) forced the interrupt
|
||||
enable for the ARM core to a one, ARM core interrupts are disabled.
|
||||
The next blinks at 2 seconds per state change poll the interrupt
|
||||
status register at this layer.
|
||||
|
||||
The last step is to enable the interrupts to the arm core by changing
|
||||
the I bit in the cpsr to a zero. Because we had prepared the exception
|
||||
table when the interrupt comes the code at address 0x1C is executed
|
||||
which eventually hits the c_irq_handler function, remember this is
|
||||
an interrupt handler, you are soemwhat running parallel. Dont take
|
||||
very long and dont mess with resources in use by the foreground
|
||||
application. typically you want to be in and out fast. At some
|
||||
point in the isr you need to clear the source of the isr.
|
||||
|
||||
139
pizero/blinker05/blinker05.c
Normal file
139
pizero/blinker05/blinker05.c
Normal file
@@ -0,0 +1,139 @@
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
extern void PUT32 ( unsigned int, unsigned int );
|
||||
extern unsigned int GET32 ( unsigned int );
|
||||
extern void dummy ( unsigned int );
|
||||
extern void enable_irq ( void );
|
||||
extern void enable_fiq ( void );
|
||||
|
||||
|
||||
#define ARM_TIMER_LOD 0x2000B400
|
||||
#define ARM_TIMER_VAL 0x2000B404
|
||||
#define ARM_TIMER_CTL 0x2000B408
|
||||
#define ARM_TIMER_CLI 0x2000B40C
|
||||
#define ARM_TIMER_RIS 0x2000B410
|
||||
#define ARM_TIMER_MIS 0x2000B414
|
||||
#define ARM_TIMER_RLD 0x2000B418
|
||||
#define ARM_TIMER_DIV 0x2000B41C
|
||||
#define ARM_TIMER_CNT 0x2000B420
|
||||
|
||||
#define SYSTIMERCLO 0x20003004
|
||||
#define GPFSEL1 0x20200004
|
||||
#define GPSET0 0x2020001C
|
||||
#define GPCLR0 0x20200028
|
||||
#define GPFSEL3 0x2020000C
|
||||
#define GPFSEL4 0x20200010
|
||||
#define GPSET1 0x20200020
|
||||
#define GPCLR1 0x2020002C
|
||||
|
||||
#define IRQ_BASIC 0x2000B200
|
||||
#define IRQ_PEND1 0x2000B204
|
||||
#define IRQ_PEND2 0x2000B208
|
||||
#define IRQ_FIQ_CONTROL 0x2000B210
|
||||
#define IRQ_ENABLE_BASIC 0x2000B218
|
||||
#define IRQ_DISABLE_BASIC 0x2000B224
|
||||
|
||||
volatile unsigned int icount;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void c_irq_handler ( void )
|
||||
{
|
||||
icount++;
|
||||
if(icount&1)
|
||||
{
|
||||
PUT32(GPSET1,1<<(47-32));
|
||||
//PUT32(GPSET1,1<<(35-32));
|
||||
}
|
||||
else
|
||||
{
|
||||
PUT32(GPCLR1,1<<(47-32));
|
||||
//PUT32(GPCLR1,1<<(35-32));
|
||||
}
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
int notmain ( void )
|
||||
{
|
||||
unsigned int ra;
|
||||
|
||||
PUT32(IRQ_DISABLE_BASIC,1);
|
||||
|
||||
ra=GET32(GPFSEL4);
|
||||
ra&=~(7<<21);
|
||||
ra|=1<<21;
|
||||
PUT32(GPFSEL4,ra);
|
||||
|
||||
//ra=GET32(GPFSEL3);
|
||||
//ra&=~(7<<15);
|
||||
//ra|=1<<15;
|
||||
//PUT32(GPFSEL3,ra);
|
||||
|
||||
PUT32(GPSET1,1<<(47-32));
|
||||
//PUT32(GPCLR1,1<<(35-32));
|
||||
|
||||
PUT32(ARM_TIMER_CTL,0x003E0000);
|
||||
PUT32(ARM_TIMER_LOD,1000000-1);
|
||||
PUT32(ARM_TIMER_RLD,1000000-1);
|
||||
PUT32(ARM_TIMER_DIV,0x000000F9);
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
PUT32(ARM_TIMER_CTL,0x003E00A2);
|
||||
|
||||
for(ra=0;ra<2;ra++)
|
||||
{
|
||||
PUT32(GPSET1,1<<(47-32));
|
||||
//PUT32(GPCLR1,1<<(35-32));
|
||||
while(1) if(GET32(ARM_TIMER_MIS)) break;
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
PUT32(GPCLR1,1<<(47-32));
|
||||
//PUT32(GPSET1,1<<(35-32));
|
||||
while(1) if(GET32(ARM_TIMER_MIS)) break;
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
}
|
||||
|
||||
PUT32(ARM_TIMER_CTL,0x003E0000);
|
||||
PUT32(ARM_TIMER_LOD,2000000-1);
|
||||
PUT32(ARM_TIMER_RLD,2000000-1);
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
PUT32(IRQ_ENABLE_BASIC,1);
|
||||
PUT32(ARM_TIMER_CTL,0x003E00A2);
|
||||
for(ra=0;ra<3;ra++)
|
||||
{
|
||||
PUT32(GPSET1,1<<(47-32));
|
||||
//PUT32(GPCLR1,1<<(35-32));
|
||||
while(1) if(GET32(IRQ_BASIC)&1) break;
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
PUT32(GPCLR1,1<<(47-32));
|
||||
//PUT32(GPSET1,1<<(35-32));
|
||||
while(1) if(GET32(IRQ_BASIC)&1) break;
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
}
|
||||
|
||||
|
||||
PUT32(ARM_TIMER_CTL,0x003E0000);
|
||||
PUT32(ARM_TIMER_LOD,4000000-1);
|
||||
PUT32(ARM_TIMER_RLD,4000000-1);
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
icount=0;
|
||||
enable_irq();
|
||||
PUT32(ARM_TIMER_CTL,0x003E00A2);
|
||||
PUT32(ARM_TIMER_CLI,0);
|
||||
while(1) continue;
|
||||
return(0);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2012 David Welch dwelch@dwelch.com
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
11
pizero/blinker05/memmap
Normal file
11
pizero/blinker05/memmap
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ram : ORIGIN = 0x8000, LENGTH = 0x1000
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text : { *(.text*) } > ram
|
||||
.bss : { *(.bss*) } > ram
|
||||
}
|
||||
96
pizero/blinker05/vectors.s
Normal file
96
pizero/blinker05/vectors.s
Normal file
@@ -0,0 +1,96 @@
|
||||
|
||||
;@-------------------------------------------------------------------------
|
||||
;@-------------------------------------------------------------------------
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
ldr pc,reset_handler
|
||||
ldr pc,undefined_handler
|
||||
ldr pc,swi_handler
|
||||
ldr pc,prefetch_handler
|
||||
ldr pc,data_handler
|
||||
ldr pc,unused_handler
|
||||
ldr pc,irq_handler
|
||||
ldr pc,fiq_handler
|
||||
reset_handler: .word reset
|
||||
undefined_handler: .word hang
|
||||
swi_handler: .word hang
|
||||
prefetch_handler: .word hang
|
||||
data_handler: .word hang
|
||||
unused_handler: .word hang
|
||||
irq_handler: .word irq
|
||||
fiq_handler: .word hang
|
||||
|
||||
reset:
|
||||
mov r0,#0x8000
|
||||
mov r1,#0x0000
|
||||
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||
|
||||
|
||||
;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
|
||||
mov r0,#0xD2
|
||||
msr cpsr_c,r0
|
||||
mov sp,#0x8000
|
||||
|
||||
;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
|
||||
mov r0,#0xD1
|
||||
msr cpsr_c,r0
|
||||
mov sp,#0x4000
|
||||
|
||||
;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
|
||||
mov r0,#0xD3
|
||||
msr cpsr_c,r0
|
||||
mov sp,#0x8000000
|
||||
|
||||
;@ SVC MODE, IRQ ENABLED, FIQ DIS
|
||||
;@mov r0,#0x53
|
||||
;@msr cpsr_c, r0
|
||||
|
||||
bl notmain
|
||||
hang: b hang
|
||||
|
||||
.globl PUT32
|
||||
PUT32:
|
||||
str r1,[r0]
|
||||
bx lr
|
||||
|
||||
.globl GET32
|
||||
GET32:
|
||||
ldr r0,[r0]
|
||||
bx lr
|
||||
|
||||
.globl dummy
|
||||
dummy:
|
||||
bx lr
|
||||
|
||||
.globl enable_irq
|
||||
enable_irq:
|
||||
mrs r0,cpsr
|
||||
bic r0,r0,#0x80
|
||||
msr cpsr_c,r0
|
||||
bx lr
|
||||
|
||||
irq:
|
||||
push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
|
||||
bl c_irq_handler
|
||||
pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
|
||||
subs pc,lr,#4
|
||||
|
||||
;@-------------------------------------------------------------------------
|
||||
;@-------------------------------------------------------------------------
|
||||
|
||||
;@-------------------------------------------------------------------------
|
||||
;@
|
||||
;@ Copyright (c) 2012 David Welch dwelch@dwelch.com
|
||||
;@
|
||||
;@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
;@
|
||||
;@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
;@
|
||||
;@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
;@
|
||||
;@-------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user