Hello friends, I need your help have time programming code for 8051 (c51) in keil and have no problem but now I would like to start with arm, specifically with LPC2138 and I want to convert the code I show is written for 8051 but not to start. All I know is how to define ports and little else. I swear I've tried but have not succeeded. the code is for a led matrix display and Works fine please can help me? thank you very much carlos
//crystal is 22-24MHz //was made for Keil compiler
#include <reg51.h> #define BAUDRATE 9600 sbit Cclock=P1^1; sbit Clatch=P1^0; sbit Cdata=P3^7; sbit Row1=P1^2; sbit Row2=P1^3; sbit Row3=P1^4; sbit Row4=P1^5; sbit Row5=P1^6; sbit Row6=P1^7; sbit Row7=P3^2; sbit Row8=P3^3;
unsigned int shift_delay;
unsigned char code font[96][16] = {
{0x00, 0x1f, 0x11, 0x11, 0x00}, // {0x10, 0x08, 0x04, 0x02, 0x01}, // {0x00, 0x11, 0x11, 0x1f, 0x00}, // {0x04, 0x08, 0x10, 0x08, 0x04}, // {0x01, 0x01, 0x01, 0x01, 0x01}, // {0x00, 0x00, 0x10, 0x08, 0x00}, // {0x12, 0x15, 0x15, 0x15, 0x0f}, //a {0x1f, 0x09, 0x09, 0x09, 0x06}, //b {0x0e, 0x11, 0x11, 0x11, 0x0a}, //c {0x06, 0x09, 0x09, 0x09, 0x1f}, //d {0x0e, 0x15, 0x15, 0x15, 0x0d}, //e {0x0f, 0x14, 0x14, 0x10, 0x10}, //f {0x09, 0x15, 0x15, 0x15, 0x0e}, //g {0x1f, 0x08, 0x08, 0x08, 0x07}, //h {0x00, 0x00, 0x17, 0x00, 0x00}, //i {0x01, 0x01, 0x01, 0x01, 0x1e}, //j {0x1f, 0x04, 0x04, 0x0a, 0x11}, //k {0x1e, 0x01, 0x01, 0x01, 0x01}, //l {0x0f, 0x10, 0x0f, 0x10, 0x0f}, //m {0x1f, 0x10, 0x10, 0x10, 0x0f}, //n {0x0e, 0x11, 0x11, 0x11, 0x0e}, //o {0x1f, 0x12, 0x12, 0x12, 0x0c}, //p {0x0c, 0x12, 0x12, 0x12, 0x1f}, //q {0x1f, 0x08, 0x10, 0x10, 0x08}, //r {0x09, 0x15, 0x15, 0x15, 0x12}, //s {0x1e, 0x09, 0x09, 0x01, 0x02}, //t {0x1e, 0x01, 0x01, 0x02, 0x1f}, //u {0x18, 0x06, 0x01, 0x06, 0x18}, //v {0x1e, 0x01, 0x1e, 0x01, 0x1e}, //w {0x1b, 0x04, 0x04, 0x04, 0x1b}, //x {0x19, 0x05, 0x05, 0x05, 0x1e}, //y {0x11, 0x13, 0x15, 0x19, 0x11}, //z {0x00, 0x04, 0x0e, 0x11, 0x00}, // {0x00, 0x00, 0x1f, 0x00, 0x00}, // {0x00, 0x11, 0x0e, 0x04, 0x00}, // {0x08, 0x10, 0x08, 0x04, 0x08}, // {0x00, 0x00, 0x00, 0x00, 0x00} // }; unsigned char column[41]= { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; //display memory, cleared before used.
void delayms(unsigned int ms) //just about 1ms { unsigned int n; unsigned int i; for (n=0; n<ms; n++) { for (i=0; i<200; i++); } }
void init_timer(void)//config timer for 6.7ms { TMOD=0x01; TH0=0xf8; TL0=0x00; TR0=1; ET0=1; EA=1; }
void Timer0 (void) interrupt 1 //using 2 { unsigned int index; unsigned int i,tmp1; unsigned char code rowtable[]={0,1,2,4,8,16,32,64,128}; TH0=0xf8; TL0=0x00; ET0=0; Row8=1; Row7=1; Row6=1; Row5=1; Row4=1; Row3=1; Row2=1; Row1=1; index++; if (index>8) index=1;
//column tmp1=rowtable[index]; for (i=1; i<41; i++) { Cdata=!(column[i] & tmp1); Cclock=0; Cclock=1; } //row Clatch=0; Clatch=1; switch (index) { case 1: Row1=0; break; case 2: Row2=0; break; case 3: Row3=0; break; case 4: Row4=0; break; case 5: Row5=0; break; case 6: Row6=0; break; case 7: Row7=0; break; case 8: Row8=0; break;
} ET0=1; }
void LED_put_byte(unsigned int inp) { unsigned char i; for (i=1;i<41;i++) column[i]=column[i+1]; column[40]=inp; }
void LED_puts(unsigned char *lcd_string) { unsigned char i,tmp_chr; while (*lcd_string) { tmp_chr=*lcd_string; for (i=0;i<6;i++) { LED_put_byte(font[tmp_chr-32][i]); delayms(shift_delay); } LED_put_byte(0); //space between character delayms(shift_delay); lcd_string++; } }
void main() { unsigned char m[40], i; TMOD=0x20; // Timer 1, mode-2 (8-bit auto reload) TH1=0xFA; //9600 baud rate SCON=0x50; TR1=1; //start timer 1
/* read 10 bytes or till enter key */ for (i = 0; i < 240; i++) { while (RI == 0); //wait to receive data if (SBUF == '\r') break; m[i] = SBUF; // save value of data RI = 0;
} m[i] = '\0';
init_timer(); shift_delay=40; //bigger = slower shift while(1) {
// display m LED_puts(m); } }
If you know how to define ports, then you should also be able to toggle processor pins.
Want to use the timer? The processor user manual and available examples is obviously a good start.
Want to use a serial port? The processor user manual and available examples are obviously good to start with.
All great journeys starts with some small steps. Work yourself through existing examples and compare with the examples does with corresponding documentation in the processor user manual.
Just note that LPC21xx is a rather old processor family that was replaced by the LPC23xx family that later got updated with the LPC17xx family. If you have the option to switch, it would be better to look at a LPC17xx - at least from a commercial perspective it's way better to gain knowledge of a processor family that is recommended for new designs.
dear friend you are right but the examples I've seen more I get confused and lose myself. I do not want hagais work but as to start or some idea. with timer and uart thanks
Take an example that uses some I/O pins and an UART.
Read the code. Read the user manual for the I/O pins and the UART. Read the code. Read the user manual for the I/O pins and the UART.
Now you should be able to understand and "translate" the meaning of the I/O-related and UART-related code lines.
And you should have also picked up information from the user manual that the the processor can do even more things with the I/O pins and much more things with the UART. Things that are good to remember when you later need to solve more complex problems.
So switch to an existing example that uses a timer. Same there. Cross-correlate the use of timer registers with the user manual chapter about timers. Repeat two or three times. Then you should be able to understand how to initialize a timer. What values you can write to different registers, and what the result will be.
Jumping between the example code and the processor documentation is the only way you can learn. If I post source code, you will not understand that code any more than you understand all the already existing example programs. Understanding only comes from using the processor documentation as a wikipedia and look up exactly what different source lines is actually doing. And after a couple of iterations you will not only know what a line does, but also start to understand why it is needed.
When you do understand "what" and "why", then you have reached the level where you can take existing sample code and modify to solve a different problem. Which also means that you have reached the level where you can start with your own project and implement the individual sub-functions you require. With the sub-function implemented, the rest of the code doesn't care much what actual architecture you are using.
ok I'll try. I'll tell you my progress Thanks
The ARM architecture is quite different from the 8051 - so it's probably not a great idea to try to just "convert" the code...
Indeed, take the time to analyze what your current code does, and the mechanics of it, and then re-implement it for your ARM chip.
There really is no substitute for knowing what you're doing.
Sorry had to dash off.
The ARM is not just a "bigger 8051" - so trying to treat it that way is likely to lead to frustration.
Also, the Keil ARM toolset is not just a "bigger C51".
So the way to proceed is to start from basics to understand your chip, its peripherals, and the tools.
Then you will need to distil-out what you code does (requirements) - as distinct from how it does it (implementation).
When you know what you old code does, then you can think about how to achieve the same ends with the new chip & toolset (same requirements, but new implementation).
This is the new code and not works I need help please Thanks #include <LPC21xx.h> #include <stdio.h> #define Cclock 0 #define Clatch 1 #define Cdata 2 #define Row1 3 #define Row2 4 #define Row3 5 #define Row4 6 #define Row5 7 #define Row6 8 #define Row7 9 #define Row8 10 unsigned int shift_delay; const unsigned char font[96][16] = { {0x00, 0x1f, 0x11, 0x11, 0x00}, // {0x10, 0x08, 0x04, 0x02, 0x01}, // {0x00, 0x11, 0x11, 0x1f, 0x00}, // {0x04, 0x08, 0x10, 0x08, 0x04}, // {0x01, 0x01, 0x01, 0x01, 0x01}, // {0x00, 0x00, 0x10, 0x08, 0x00}, // {0x12, 0x15, 0x15, 0x15, 0x0f}, //a {0x1f, 0x09, 0x09, 0x09, 0x06}, //b {0x0e, 0x11, 0x11, 0x11, 0x0a}, //c {0x06, 0x09, 0x09, 0x09, 0x1f}, //d {0x0e, 0x15, 0x15, 0x15, 0x0d}, //e {0x0f, 0x14, 0x14, 0x10, 0x10}, //f {0x09, 0x15, 0x15, 0x15, 0x0e}, //g {0x1f, 0x08, 0x08, 0x08, 0x07}, //h {0x00, 0x00, 0x17, 0x00, 0x00}, //i {0x01, 0x01, 0x01, 0x01, 0x1e}, //j {0x1f, 0x04, 0x04, 0x0a, 0x11}, //k {0x1e, 0x01, 0x01, 0x01, 0x01}, //l {0x0f, 0x10, 0x0f, 0x10, 0x0f}, //m {0x1f, 0x10, 0x10, 0x10, 0x0f}, //n {0x0e, 0x11, 0x11, 0x11, 0x0e}, //o {0x1f, 0x12, 0x12, 0x12, 0x0c}, //p {0x0c, 0x12, 0x12, 0x12, 0x1f}, //q {0x1f, 0x08, 0x10, 0x10, 0x08}, //r {0x09, 0x15, 0x15, 0x15, 0x12}, //s {0x1e, 0x09, 0x09, 0x01, 0x02}, //t {0x1e, 0x01, 0x01, 0x02, 0x1f}, //u {0x18, 0x06, 0x01, 0x06, 0x18}, //v {0x1e, 0x01, 0x1e, 0x01, 0x1e}, //w {0x1b, 0x04, 0x04, 0x04, 0x1b}, //x {0x19, 0x05, 0x05, 0x05, 0x1e}, //y {0x11, 0x13, 0x15, 0x19, 0x11}, //z {0x00, 0x04, 0x0e, 0x11, 0x00}, // {0x00, 0x00, 0x1f, 0x00, 0x00}, // {0x00, 0x11, 0x0e, 0x04, 0x00}, // {0x08, 0x10, 0x08, 0x04, 0x08}, // {0x00, 0x00, 0x00, 0x00, 0x00} // }; unsigned char column[41]= { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; //display memory, cleared before used. void delayms(unsigned int ms) //just about 1ms { unsigned int n; unsigned int i; for (n=0; n<ms; n++) { for (i=0; i<250; i++); } } __irq void Timer0_irq(void) { T0PR = 0x00000000; //Pre-Scalar is Zero T0MR0 = 39000L; //Value Loaded in MR0 Register(Match Register) T0MCR = 3; T0TC = 0x00; //Value in Timer-Control Register T0TCR = 0x00000001; //Start Timer-0 VICVectAddr4 = (unsigned )Timer0_irq; VICVectCntl4 = (0x20 | 4); VICIntEnable = (1UL << 4); //Enable Timer0 Interrupt } void Timer0 (void) {
unsigned int index; unsigned int i,tmp1; const unsigned char rowtable[]={0,1,2,4,8,16,32,64,128};
IOSET0 |= (1<<Row8)|(1<<Row7)|(1<<Row6)|(1<<Row5)|(1<<Row4)|(1<<Row3)|(1<<Row2)|(1<<Row1)| index++; if (index>8) index=1; tmp1=rowtable[index]; for (i=1; i<41; i++) { IOSET0|=(1<<Cdata)&(column[i] & tmp1); IOSET0 |= (1<<Cclock); IOCLR0 |= (1<<Cclock);
} IOSET0 |= (1<<Clatch); IOCLR0 |= (1<<Clatch); switch (index) { case 1: IOCLR0 |= (1<<Row1); break; case 2: IOCLR0 |= (1<<Row2); break; case 3: IOCLR0 |= (1<<Row3); break; case 4: IOCLR0 |= (1<<Row4); break; case 5: IOCLR0 |= (1<<Row5); break; case 6: IOCLR0 |= (1<<Row6); break; case 7: IOCLR0 |= (1<<Row7); break; case 8: IOCLR0 |= (1<<Row8); break; } VICVectAddr = 0; }
void LED_put_byte(unsigned int inp) { unsigned char i; for (i=1;i<41;i++) column[i]=column[i+1]; column[40]=inp; } void LED_puts(unsigned char *lcd_string) { unsigned char i,tmp_chr; while (*lcd_string) { tmp_chr=*lcd_string; for (i=0;i<6;i++) { LED_put_byte(font[tmp_chr-32][i]); delayms(shift_delay); } LED_put_byte(0); //space between character delayms(shift_delay); lcd_string++; } } int main(void) { IODIR0 |= (1<<Cclock); IODIR0 |= (1<<Clatch); IODIR0 |= (1<<Cdata); IODIR0 |= (1<<Row1); IODIR0 |= (1<Row2); IODIR0 |= (1<<Row3); IODIR0 |= (1<<Row4); IODIR0 |= (1<<Row5); IODIR0 |= (1<Row6); IODIR0 |= (1<<Row7); IODIR0 |= (1<<Row8); Timer0 ();
shift_delay=50; //bigger = slower shift while(1) {
// display m LED_puts("Hello"); } }
So start debugging it, then!
Did you follow Per's advice to study the processor documentation and examples, and to work with each peripheral until you understand it?
When I see things like this, I know that the manual hasn't been pondered very much:
IOSET0 |= (1<<Cclock);
Exactly what would the |= do in that statement? Exactly what does the register IOSET0 do?
__irq void Timer0_irq(void) { T0PR = 0x00000000; //Pre-Scalar is Zero T0MR0 = 39000L; //Value Loaded in MR0 Register(Match Register) T0MCR = 3; T0TC = 0x00; //Value in Timer-Control Register T0TCR = 0x00000001; //Start Timer-0 VICVectAddr4 = (unsigned )Timer0_irq; VICVectCntl4 = (0x20 | 4); VICIntEnable = (1UL << 4); //Enable Timer0 Interrupt }
The above is not the correct contents for a timer interrupt - the above is code for initializing the timer. Having a timer interrupt that on every interrupt sets the interrupt vector to itself is silly - especially since the ISR can't be called without someone else having first initialized the timer.
Having mixed up the function Timer0() and the Timer0_irq() clearly indicates how little (zero) time you have spent trying to analyze your program to find out why it doesn't work. You really think that is a good approach?
void delayms(unsigned int ms) //just about 1ms { unsigned int n; unsigned int i; for (n=0; n<ms; n++) { for (i=0; i<250; i++); } }
Just about 1ms? How do you know? Don't you think the processor got timers because the timers are specifically adapted for timing? So why do you try a 1ms delay as a busy-loop?
I've been working hard in the code but can not get result sorry for my ignorance
#include <LPC21xx.h> #include <stdio.h>
unsigned int shift_delay; const unsigned char font[96][16] = {
{0x00, 0x1f, 0x11, 0x11, 0x00}, // {0x10, 0x08, 0x04, 0x02, 0x01}, // {0x00, 0x11, 0x11, 0x1f, 0x00}, // {0x04, 0x08, 0x10, 0x08, 0x04}, // {0x01, 0x01, 0x01, 0x01, 0x01}, // {0x00, 0x00, 0x10, 0x08, 0x00}, // {0x12, 0x15, 0x15, 0x15, 0x0f}, //a {0x1f, 0x09, 0x09, 0x09, 0x06}, //b {0x0e, 0x11, 0x11, 0x11, 0x0a}, //c {0x06, 0x09, 0x09, 0x09, 0x1f}, //d {0x0e, 0x15, 0x15, 0x15, 0x0d}, //e {0x0f, 0x14, 0x14, 0x10, 0x10}, //f {0x09, 0x15, 0x15, 0x15, 0x0e}, //g {0x1f, 0x08, 0x08, 0x08, 0x07}, //h {0x00, 0x00, 0x17, 0x00, 0x00}, //i {0x01, 0x01, 0x01, 0x01, 0x1e}, //j {0x1f, 0x04, 0x04, 0x0a, 0x11}, //k {0x1e, 0x01, 0x01, 0x01, 0x01}, //l {0x0f, 0x10, 0x0f, 0x10, 0x0f}, //m {0x1f, 0x10, 0x10, 0x10, 0x0f}, //n {0x0e, 0x11, 0x11, 0x11, 0x0e}, //o {0x1f, 0x12, 0x12, 0x12, 0x0c}, //p {0x0c, 0x12, 0x12, 0x12, 0x1f}, //q {0x1f, 0x08, 0x10, 0x10, 0x08}, //r {0x09, 0x15, 0x15, 0x15, 0x12}, //s {0x1e, 0x09, 0x09, 0x01, 0x02}, //t {0x1e, 0x01, 0x01, 0x02, 0x1f}, //u {0x18, 0x06, 0x01, 0x06, 0x18}, //v {0x1e, 0x01, 0x1e, 0x01, 0x1e}, //w {0x1b, 0x04, 0x04, 0x04, 0x1b}, //x {0x19, 0x05, 0x05, 0x05, 0x1e}, //y {0x11, 0x13, 0x15, 0x19, 0x11}, //z {0x00, 0x04, 0x0e, 0x11, 0x00}, // {0x00, 0x00, 0x1f, 0x00, 0x00}, // {0x00, 0x11, 0x0e, 0x04, 0x00}, // {0x08, 0x10, 0x08, 0x04, 0x08}, // {0x00, 0x00, 0x00, 0x00, 0x00} // }; unsigned char column[41]= { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; //display memory, cleared before used. void delayms(unsigned int ms) //just about 1ms { unsigned int n; unsigned int i; for (n=0; n<ms; n++) { for (i=0; i<6000; i++); } }
void init_timer(void)
{ __irq void Timer0_IRQ(void); //Port-0.0 tp Port-0.7 pins as Output Pin
//Timer-0 Configuration T0PR = 0x00000000; //Pre-Scalar is Zero T0MR0 = 375000L; //Value Loaded in MR0 Register(Match Register) //500 msec Delay
T0MCR = 3; //Match Control Register //When TC = MR0, MR0 interrupt will Occur
T0TC = 0x00; //Value in Timer-Control Register T0TCR = 0x00000001; //Start Timer-0
VICVectAddr4 = (unsigned)Timer0_IRQ; VICVectCntl4 = (0x20 | 4); VICIntEnable = (1UL << 4); //Enable Timer0 Interrupt
} __irq void Timer0_IRQ(void) { unsigned int Cdata; unsigned int index; unsigned int i,tmp1; unsigned char rowtable[]={0,1,2,4,8,16,32,64,128}; IODIR0|=(1 << 3) | (1 << 4)| (1 << 5)| (1 << 6)| (1 << 7)| (1 << 8)| (1 << 9)| (1 << 10)| (1 << 2)| (1 << 1)| (1 << 0); IOSET0|= (1 << 3) | (1 << 4)| (1 << 5)| (1 << 6)| (1 << 7)| (1 << 8)| (1 << 9)| (1 << 10); index++; if (index>8) index=1; //column tmp1=rowtable[index]; for (i=1; i<41; i++) { Cdata=!(column[i] & tmp1); IOSET0=(1<<2)&Cdata;
IOCLR0=(1 << 0); IOSET0=(1 << 0); } IOCLR0=(1 << 1); IOSET0=(1 << 1); switch (index) { case 1: IOCLR0=(1 << 10); break; case 2: IOCLR0=(1 << 9); break; case 3: IOCLR0=(1 << 8); break; case 4: IOCLR0=(1 << 7); break; case 5: IOCLR0=(1 << 6); break; case 6: IOCLR0=(1 << 5); break; case 7: IOCLR0=(1 << 4); break; case 8: IOCLR0=(1 << 3); break;
} T0IR = (1UL<<0);
} void LED_put_byte(unsigned int inp) { unsigned char i; for (i=1;i<41;i++) column[i]=column[i+1]; column[40]=inp; }
void LED_puts(unsigned char *lcd_string) { unsigned char i,tmp_chr; while (*lcd_string) { tmp_chr=*lcd_string; for (i=0;i<6;i++) { LED_put_byte(font[tmp_chr-32][i]); delayms(shift_delay); } LED_put_byte(0); //space between character delayms(shift_delay); lcd_string++; } } int main() { init_timer(); shift_delay=100; //bigger = slower shift while(1) { LED_puts("8x32 Matrix LED: AT89C2051, 74HC595");
} }
Please read posting instructions, use PRE tags for source code, otherwise you get an unreadable wall of text - Thanks
http://www.keil.com/forum/tips.asp
But the advice was not to dive straight into coding the whole thing!
The advice was to study first - so that you understand the new chip, its peripherals, and its tools.
Did you do that?
Notice the difference in formatting between the code I posted, and the code you have been posting?
There is a very important reason why - it would be good if you did read up exactly on how to post source code on this forum.
_irq void Timer0_IRQ(void) { unsigned int Cdata; unsigned int index; unsigned int i,tmp1; unsigned char rowtable[]={0,1,2,4,8,16,32,64,128}; ^^^ You want to assign these values to your rowtable[] array on every single call ^^^ to your interrupt handler? You think that makes it faster? ^^^ Why isn't rowtable[] a constant? IODIR0|=(1 << 3) | (1 << 4)| (1 << 5)| (1 << 6)| (1 << 7)| (1 << 8) | (1 << 9)| (1 << 10)| (1 << 2)| (1 << 1)| (1 << 0); IOSET0|= (1 << 3) | (1 << 4)| (1 << 5)| (1 << 6)| (1 << 7)| (1 << 8) | (1 << 9)| (1 << 10); ^^^ You still haven't been thinking about the fact that |= represents a ^^^ read-modify-write operation. And exactly what is the result of reading ^^^ from the IOSET0 register? Have you spent zero time with the processor ^^^ user manual? Don't you know that that makes the engineers who have spent ^^^ a huge amount of time to write that manual very sad - you ignore their ^^^ work. index++; if (index>8) index=1; //column tmp1=rowtable[index]; for (i=1; i<41; i++) { Cdata=!(column[i] & tmp1); IOSET0=(1<<2)&Cdata; ^^^ This time you do not do |= ^^^ Any reason why not here, while you did when assigning to IOSET0 the ^^^ previous time? IOCLR0=(1 << 0); IOSET0=(1 << 0); } IOCLR0=(1 << 1); IOSET0=(1 << 1); ^^^ Isn't it silly that it can't be seen from the above lines exactly ^^^ what they do? ^^^ Wouldn't it be way better if your code looked like: ^^^ IOCLR0 = (1u << SIGNAL_NAME); ^^^ IOSET0 = (1u << SIGNAL_NAME); ^^^ So a reader understands exactly which signal you are toggling? ^^^ Which also makes sure that _you_ understand which signal you ^^^ are toggling... switch (index) { case 1: IOCLR0=(1 << 10); break; case 2: IOCLR0=(1 << 9); break; case 3: IOCLR0=(1 << 8); break; case 4: IOCLR0=(1 << 7); break; case 5: IOCLR0=(1 << 6); break; case 6: IOCLR0=(1 << 5); break; case 7: IOCLR0=(1 << 4); break; case 8: IOCLR0=(1 << 3); break; } ^^^ Your code is time-critical. ^^^ Any reason why your above switch statement isn't changed into a single ^^^ assign to IOCLR0, with a computed value - possibly from a lookup in case ^^^ you don't trust the compiler/processor to be able to barrel-rotate the ^^^ specific bit to the correct position in a single step? T0IR = (1UL<<0); ^^^ One more of all magic constants. ^^^ Exactly what is the meaning of 1UL << 0 here? }
By the way - you are still guessing about the delay of your delay function instead of having the processor compute x milliseconds of delay using a timer.
www.at91.com/.../