"$MCLR$ is low. Processor is in reset." Error on Pic18 - c

On this circuit and code, I tried to make a counter that when no one pass through (for example it is a passage at the metro station), there will read 1 value at RC7 lead of the processor. If someone pass through the signal change to 0. And program will count the how many people pass away over there and show the number of people on the 7-Segment LCD until 10. When 10 people pass through of the passage, the LED (D1) will be blinking for 1 seconds.
I tried to write a code about this algorithm, and when I load it to Pic18F45K22 but, it is not working. Proteus show error message like,
[PIC18] PC=0x0000. $MCLR$ is low. Processor is in reset. [U1]
The circuit that I designed given at below Figure 1:
The solutions that I tried:
I used pull-up resistors. It did not work.
We describe the frequency value in Micro C code. It did not work.
And the algorithm given at below:
#include <xc.h>
#define _XTAL_FREQ 4000000
unsigned char x=0;
void MSDelay(unsigned int);
void main()
{
TRISC=0xff;
TRISA=0x00;
while(1)
{
if (PORTC==0)
{
x++;
MSDelay(200);
}
if (x==1)
{
PORTA==0x3f;
}
if (x==2)
{
PORTA==0x06;
}
if (x==3)
{
PORTA==0x5b;
}
if (x==4)
{
PORTA==0x4f;
}
if (x==5)
{
PORTA==0x66;
}
if (x==6)
{
PORTA==0x6d;
}
}
}
void MSDelay(unsigned int itime){ //for delay
unsigned int i;
unsigned int j;
for(i=0;i<itime;i++){
for(j=0;j<165;j++){
}
}
}

So I make the answer for you:
The error ist here:
if (x==1)
{
PORTA==0x3f;
}
If you want assign a value you need = and not ==
if (x==1)
{
PORTA=0x3f;
}

Related

Seven segment project using atmega16 and external interrupts

I have been working on a simple project on AVR microcontroller atmega16 on proteus simulation.
The project is:
When I push the push button it increments the seven segment to number 9 and after that if the we pushed the button again it overflows and return again to 0.
When I change the (flag=1;) position at while loop code it gives me different outputs and by this I mean when I push the push button it didn't response after the pushing.
It may require another pushing to the button to increment the seven segment proteus simulation
The only code that worked properly is when set flag=1; before exiting (the second if and else conditions)
So my question is what actually happened when I change the flag=1; statement position at the code.
#include<avr/io.h>
#include<avr/interrupt.h>
char flag=1;
char num=0;
void INT2_Init(void){
DDRB&=~(1<<2);
SREG|=(1<<7);
GICR|=(1<<INT2);
MCUCSR|=(1<<ISC2);
}
ISR(INT2_vect){
flag=0;
GIFR|=(1<<INTF2);
}
int main(void){
DDRC=0x0f;
PORTC=0;
DDRB&=~(1<<2);
INT2_Init();
while(1){
if(flag==0){
if(num>8){
num=0;
PORTC=(PORTC&0xf0)|num;
}
else{
num++;
PORTC=(PORTC&0xf0)|num;
}
}
flag=1;
}
}
If some variable changes in main code and interrupt, you have to mark it volatile. Otherwise, the compiler may decide to cache variable value and omit its reading inside a loop.
volatile char flag=1;
Second, pay attention when and where your flag variables are changed. E.g.:
while(1){
// at this point flag is 0, comparison failed
if(flag==0) {
...
}
// Interrupt happens here, changing flag to 0
// Then flag is immediately reset, thus flag change is missed in the loop
flag=1;
}
Instead consider to use such a pattern:
while (1) {
cli(); // disabling interrupts ensuring no interrupt happens inside
if (flag==0) {
...
}
flag = 1;
sei(); // re-enable interrupts
}
or, in this case, it can be simpler
while (1) {
if (flag==0) {
flag = 1; // Reset flag only if it set, right after comparison
...
}
}
It is not entirely clear to me what you are asking. Do you just struggle to understand the code? Do my comments help?
#include<avr/io.h>
#include<avr/interrupt.h>
char flag=1;
char num=0;
void INT2_Init(void){
DDRB&=~(1<<2);
SREG|=(1<<7);
GICR|=(1<<INT2);
MCUCSR|=(1<<ISC2);
}
// sets flag to 0 on external interrupt (button press)
ISR(INT2_vect){
flag=0;
GIFR|=(1<<INTF2);
}
int main(void){
DDRC=0x0f;
PORTC=0;
DDRB&=~(1<<2);
INT2_Init();
while(1){
// if flag was set to 0 in external interrupt -> increment (or overflow) number on segment display
if(flag==0){
if(num>8){
num=0;
PORTC=(PORTC&0xf0)|num;
}
else{
num++;
PORTC=(PORTC&0xf0)|num;
}
}
// reset the flag to 1 to prevent num from being incremented continuously
flag=1;
}
}
I would suggest to move flag=1; inside the if to make the intent clear that the statement should only execute once with every button press.
while(1) {
// if flag was set to 0 in external interrupt -> increment (or overflow) number on segment display
if(flag==0){
// reset the flag to 1 to prevent num from being incremented continuously
flag=1;
if(num>8){
num=0;
PORTC=(PORTC&0xf0)|num;
}
else{
num++;
PORTC=(PORTC&0xf0)|num;
}
}
}
It is also advisable to use self descriptive variable names. Like buttonWasPressed instead of flag for example. That makes code like that much easier to read.

Check only last 3 digits of sensor output

I have a library from WiringPi for DHT11 sensor and I need to modify condition which checks if the value read from sensor is good.
Sometimes the library reads bad values which are 255.255,255.255 or 55,255.255 etc.
sample output
There is the condition in the library:
if(counter==255)
break;
But it doesn't work if the value is e.g. 55,255.255
How can I modify this condition the check last 3 digits of output?
If the output is wrong, there are always "255" at the end of value.
I tried to add conditions like
if(counter==255)
break;
else if(counter==255.255)
break;
But it doesn't solve all possible situations and I realy don't know anything about C/C++
Here is the whole library:
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define MAX_TIME 85
#define DHT11PIN 7
#define ATTEMPTS 5
int dht11_val[5]={0,0,0,0,0};
int dht11_read_val()
{
uint8_t lststate=HIGH;
uint8_t counter=0;
uint8_t j=0,i;
for(i=0;i<5;i++)
dht11_val[i]=0;
pinMode(DHT11PIN,OUTPUT);
digitalWrite(DHT11PIN,LOW);
delay(18);
digitalWrite(DHT11PIN,HIGH);
delayMicroseconds(40);
pinMode(DHT11PIN,INPUT);
for(i=0;i<MAX_TIME;i++)
{
counter=0;
while(digitalRead(DHT11PIN)==lststate){
counter++;
delayMicroseconds(1);
if(counter==255)
break;
}
lststate=digitalRead(DHT11PIN);
if(counter==255)
break;
// top 3 transistions are ignored
if((i>=4)&&(i%2==0)){
dht11_val[j/8]<<=1;
if(counter>16)
dht11_val[j/8]|=1;
j++;
}
}
// verify checksum and print the verified data
if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF)))
{
printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);
return 1;
}
else
return 0;
}
int main(void)
{
int attempts=ATTEMPTS;
if(wiringPiSetup()==-1)
exit(1);
while(attempts)
{
int success = dht11_read_val();
if (success) {
break;
}
attempts--;
delay(500);
}
return 0;
}
No single variable in your code can hold "255.255", that would require a string or a float. You are obviously referring to the output of
printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);.
This printf can never produce a three-value output like 55,255.255.
I assume that your output would be 55.255,255.255.
This in turn means that in case of error you will find the "last three digits" in dht11_val[3].
If my assumption is not correct please provide much more detail on the error circumstances.
On the other hand, I suspect that looking for that value is not the solution for your problem either. The function is more complicated. The value of 255 seems the result of an endless loop which is detected by breaking early at counter = 255. So I am pretty sure that checking "the last three digits" is a LESS precise check than what is already implemented.

program for interpretation of a simple instruction set

I have a problem I need to solve and I have no freaking idea how to do it. If someone would be willing to help I would very much appreciate it. I know I'm asking for a lot, but I really need it.
Create a program for interpretation of a simple instruction set consisting of the instructions: MVI, MOV, AND, OR, NOT, LESS, LEQ, GRE, GEQ, JMP, PRN, SUM, SUB, PRB, SL and SR, described in this document. Your task is to make a program that takes as an input a binary representation of a list of instructions, and as an output it prints the corresponding result (after the execution of the instructions). The input can contain all the instructions except SUB and PRB that you do not have to implement. Conversion from binary system to any other numeral system should not be made, except at the moment when you need to find the line that should be executed next when the condition is satisfied (GRE, GEQ, LESS, LEQ, JMP), but the comparison of the numbers in the condition should be made based on the binary representatio/pn. All data are represented in SM binary system. There are eight 16-bit registers available enumerated from 0 to 7.
#include<stdio.h>
#define MAX 1000
char registers[8][16];
void MVI(int reg, char *value) {
// code here
}
void MOV(int reg1, int reg2) {
// code here
}
void AND(int reg1, int reg2, int reg3) {
// code here
}
void OR(int reg1, int reg2, int reg3) {
// code here
}
void NOT(int reg1, int reg2) {
// code here
}
void PRN(int reg) {
// code here
}
void SUM(int reg1, int reg2, int reg3) {
// code here
}
void SL(int reg) {
// code here
}
void SR(int reg) {
// code here
}
int main() {
int i,j,k;
int N = 0; // number of lines in the input
char c;
char lines[MAX][16];
while (1) {
scanf("%c", &c);
if (c == '\n') {
break;
}
lines[N][0] = c;
for (i=1;i<16;i++) {
scanf("%c", &lines[N][i]);
}
N++;
scanf("%c", &c);
}
for (i = 0; i < 8; i++) {
for (j = 0; j < 16; j++) {
registers[i][j] = '0';
}
}
// code here
return 0;
}
I think the big piece you need is dispatching the functions based on the source line. There are a number of ways you can do this, but a useful piece for all of them is strstr(a,b)==a which will check if the string a begins with the contents of the string b.
You can do a chain of if-else blocks.
if (strstr(line[i], "SUM")==line[i]){
SUM(...);
} else if (strstr(line[i], "AND")==line[i]) {
AND(...);
}
Or you can precompile the user program by scanning for the opcodes when you read the source and store them as single-byte small codes. You would want the uppercase identifiers to be enum values, and use the lowercase versions for the function names. Then the chain is simpler.
if (line[i][0] == SUM) {
SUM(...);
} else if (line[i][0] == AND) {
AND(...);
}
But, with small integer codes, there are even better ways. A switch.
switch(line[i][0]){
case SUM: sum(...); break;
case AND: and(...); break;
}
A function table. But this is where you have be clever. A function must always be called with arguments of the correct type, but function-pointers allow you to bypass the compiler's ability
to check that this is so. So for this method, all functions should have the same arguments since
they are all called by a single function-call line.
void (*optab[])(...) = { sum, and, ... };
optab[ line[i][0] ](...); // calls sum() or and() by using the opcode in the array lookup

Compiler error while programming ic

MY friend is trying to program a shift register ic 74hc595 with 8051 microcontroller attached to display a moving led message.
But my compiler is giving me error in send_data(alf(a));
Here is code->
#include<8051.h>
#define clock P2_0
#define data_bit P2_1
#define latch P2_2
#define shift 8
void delay(unsigned int i)
{
int k=0;
while(k<i)
{
k++;
}
}
void send_data(unsigned char temp)
{
unsigned char i;
unsigned char dd;
latch=0;
clock=0;
for(i=0;i<shift;i++){
dd=temp>>i;
if(dd&1)
data_bit=1;
else
data_bit=0;
clock=1;
clock=0;
}
latch=1;
}
unsigned char alf[]={16,6,6,16};
void main()
{
unsigned char a;
while(1){
for(a=0;a<4;a++)
{
send_data(alf(a));
delay(10000);
}
}
}
Since its my friend who is making, i dont have much info about it. But if anything else is needed, please tell and i will provide but please help me solve this prob.
Thanks.
send_data(alf(a));
should be
send_data(alf[a]);
On the other hand, the body of delay can be optimized (and removed) by your compiler, take a look to the volatile keyword
In your case alf is an array not a function. If alf is a function then you can call alf(a). For array you need to pass index so you need to call alf[a].

Arduino output pin outputs random activity

I am working on an Arduino project controlling a DIY RC truck that reads the output pins of an RC reciever and is supposed to PWMs a couple of pins accordingly. The pins are hooked onto a motor controller that takes PWM.
That is where the problem comes in. My reverse is working perfectly, but on the pin that does forward, I only get random activity. I'm using the Arduino Mega 2560.
Here is the code. The problem has been posted below it:
#include <Servo.h>
//Create variables for three channels
int RXCH[3];
volatile int RXSG[3];
int RXOK[3];
int PWMSG[3];
byte vooruit;
byte achteruit;
Servo stuur;
int mv = 13;
int ma = 10;
void setup() {
Serial.begin(115200);
stuur.attach(8);
//Assign PPM input pins. The receiver output pins are conected as below to non-PWM Digital connectors:
RXCH[0] = 6; //Throttle
RXCH[1] = 7; //Steering
//RXCH[2] = 5; //Nothing yet
//RXCH[3] = 2; //Nothing yet
//RXCH[4] = 7; //Nothing yet
//RXCH[5] = 8; //Nothing yet
for (int i = 0; i < 3; i++){
pinMode(RXCH[i], INPUT);
}
//TCCR1B = TCCR1B & 0b11111000 | 0x01;
//TCCR2B = TCCR2B & 0b11111000 | 0x01;
}
void loop() {
// Read RX values
for (int i = 0; i < 3; i++){ //For each of the 6 channels:
RXSG[i] = pulseIn(RXCH[i], HIGH, 20000); //Read the receiver signal
if (RXSG[i] == 0) { //Error catching
RXSG[i] = RXOK[i];
} else {
RXOK[i] = RXSG[i];
}
//Substitute the high values to a value between -255 and 255
PWMSG[0] = map(RXSG[0], 1000, 2000, -255, 255);
//Servo values, calibrated according to my steering servo.
PWMSG[1] = map(RXSG[1], 1000, 2000, 24, 169);
//Make sure that the value stays within the desired boundaries.
constrain (PWMSG[i], -255, 255);
//For debugginf purposes
Serial.print(" || Ch: ");
Serial.print(i);
Serial.print(" / PWMSG: ");
Serial.print(PWMSG[i]);
}
delay (5);
// Car goes forwards
if (PWMSG[0] > 40)
{
MV();
}
// Car goes backwards
if (PWMSG[0] < -40)
{
MA();
}
// Car stops
else
{
stopmotor();
}
stuur.write(PWMSG[1]);
Serial.println();
}
void MV()
{
vooruit = PWMSG[0];
analogWrite (mv, vooruit);
digitalWrite (ma, LOW);
Serial.print(" vooruit: ");
Serial.print(vooruit);
}
void MA()
{
achteruit = abs(PWMSG[0]);
analogWrite (ma, achteruit);
digitalWrite (mv, LOW);
Serial.print(" achteruit: ");
Serial.print(achteruit);
}
void stopmotor()
{
digitalWrite (ma, LOW);
digitalWrite (mv, LOW);
}
I don't really know if the code is considered pretty, or if I made some basic mistakes for that matter.
This is one of my first projects that I tried to do the right way, making comments and such, all well commented criticism is welcome.
What the code is supposed to do:
Move the stick on the transmitter forwards, the car goes forwards, and speed should be according to position of the stick.
Move the stick on the transmitter backwards, the car goes backwards, and speed should be according to position of the stick.
Move stick on the transmitter left or right, the servo in the car should react according to the value the Arduino calculated. You might wonder why I don't place the servo directly on the transmitter. Well, that's because I have many more future things in mind with this project, and now I can calibrate it way easier.
Problem:
When I move the stick on the transmitter forwards, and the Serial monitor is open, I get the right values on the Serial monitor, but the LED present on pin 13 just randomly blinks, very dim I must say.
I already tried to replace things like byte with int, but it had no effect. The rest of the code works fine.
Using some new code, I get a Serial response from every "stage", except the final stages that controls the pins.
#include <Servo.h>
//Create variables for channels
Servo wheel;
int MFORWARD_PIN = 13;
#define MBACKWARD_PIN 10
#define WHEEL_PIN 8
#define THROTTLE_PIN 6
#define STEERING_PIN 7
void setup() {
Serial.begin(115200);
wheel.attach(WHEEL_PIN);
pinMode(THROTTLE_PIN, INPUT);
pinMode(STEERING_PIN, INPUT);
pinMode(MFORWARD_PIN, OUTPUT);
pinMode(MBACKWARD_PIN, OUTPUT);
//TCCR1B = TCCR1B & 0b11111000 | 0x01;
//TCCR2B = TCCR2B & 0b11111000 | 0x01;
}
void loop() {
int throttle = read_throttle();
int steering = read_steering();
delay (5);
throttle_handle(throttle);
steering_handle(steering);
}
// Read RX values
int read_throttle(){
int throttle = pulseIn(THROTTLE_PIN, HIGH, 20000);
throttle = map(throttle, 1000, 2000, -255, 255); //Substitute the high values to a value between -255 and 255.
constrain (throttle, -255, 255); //Make sure that the value stays within the desired boundaries.
//Serial.println(throttle);
}
int read_steering() {
int steering = pulseIn(STEERING_PIN, HIGH, 20000);
steering = map(steering, 1000, 2000, 24, 169); //Servo values, calibrated according to my steering servo.
constrain (steering, 24, 169); //Make sure that the value stays within the disired boundaries.
//Serial.println("steering");
}
void move_forward(int val) {
analogWrite (MFORWARD_PIN, val);
digitalWrite (MBACKWARD_PIN, LOW);
Serial.print(" vooruit: ");
Serial.print(val);
}
void move_backward(int val)
{
val = abs(val);
analogWrite (MBACKWARD_PIN, val);
digitalWrite (MFORWARD_PIN, LOW);
Serial.print(" achteruit: ");
Serial.print(val);
}
void move_stop()
{
digitalWrite (MFORWARD_PIN, LOW);
digitalWrite (MBACKWARD_PIN, LOW);
}
void throttle_handle(int throttle) {
//Serial.print("throttle");
if (throttle > 40) {
move_forward(throttle);
}
if (throttle < -40) {
move_backward(throttle);
}
else {
move_stop();
}
}
void steering_handle(int steering) {
wheel.write(steering);
// Serial.println("steering:");
// Serial.print(steering);
}
Unused index:
Everywhere you loop over three values, whereas you only use two items in the array. So you'd better change all your sizes to 2 instead of 3, or you can define a NB_INPUT constant at the top of your source code, that you can change easily for more flexibility:
#define NB_INPUT 2
...
for (int i = 0; i<NB_INPUT; ++i) {
...
RXOK in setup():
your comment about arrays is justified, the first bug I can see in your code is that you read from the RXOK array, whereas you did not put any values in it. If you're sure that RXSG gets only zeroes from pulseIn() on the first pass of the Read RX values loop it can be ok, but I doubt it is. e.g.:
for (int i=0; i<3; ++i)
RXOK[i] = 0;
So you should put values in RXOK values in setup().
constant indexes in a for loop:
Then you map() the values from 1000->2000 to -255->255 for RXSG[0] and RXSG[1] inside the for loop, which will be done for the three iterations. I'm not sure what you want there, but if you want to do it for constant indexes, you'd better do it outside of the loop. But as you're checking a constraint on the -255->255 domain for each value of the iteration loop, I think you may want to do it on relative values:
PWMSG[i] = map(RXSG[i], 1000, 2000, -255, 255);
but it seems the domain is dependent on the index, so you may want to make a couple of defines at the top of your source code:
#define THROTTLE_IDX 0
#define STEERING_IDX 1
and put your map() in a if statement:
if (i == THROTTLE_IDX)
PWMSG[i] = map(RXSG[i], 1000, 2000, -255, 255);
elif (i == STEERING_IDX)
PWMSG[i] = map(RXSG[i], 1000, 2000, 24, 169);
# add a else statement if you need to do a map for the other values of the array
constrain(PWMSG[i], -255, 255)
general algorithm
I'm not sure you really need an array for your use case. You'd better keep a few variables around and have a better use of functions for your code to be readable and less bugprone:
#define THROTTLE_PIN 6
#define STEERING_PIN 7
#define WHEEL_PIN 8
#define MFORWARD_PIN 13
#define MBACKWARD_PIN 10
Servo wheel;
// sets up the arduino
void setup() {
Serial.begin(115200);
wheel.attach(WHEEL_PIN);
pinMode(THROTTLE_PIN, INPUT);
pinMode(STEERING_PIN, INPUT);
}
// input data handling
int read_throttle() {
int throttle = pulseIn(THROTTLE_PIN, HIGH, 20000);
return map(throttle, 1000, 2000, -255, 255);
}
int read_steering() {
int steering = pulseIn(STEERING_PIN, HIGH, 20000);
return map(throttle, 1000, 2000, 24, 169);
}
// output actions handling
void move_forward(int val) {
analogWrite(MFORWARD_PIN, val);
digitalWrite(MBACKWARD_PIN, LOW);
// Serial.print...
}
void move_backward(int val) {
analogWrite(MFORWARD_PIN, val);
digitalWrite(MBACKWARD_PIN, LOW);
// Serial.print...
}
void stop_motor() {
digitalWrite(MFORWARD_PIN, LOW);
digitalWrite(MBACKWARD_PIN, LOW);
}
void handle_throttle(int throttle) {
if (throttle > 40)
move_forward(throttle);
elif (throttle < -40)
move_backward(throttle);
else
stop_motor();
}
// general algorithm
void loop() {
int throttle = read_throttle();
delay(5);
handle_throttle(throttle);
}
There are more code duplication, but sometimes it is better to duplicate code than to make a code almost unreadable, and hard to debug while not offering any kind of flexibility/modularity. And here are a few other things I found in your code that should be corrected:
Naming convention: try to use good names for your functions (two letters variables, or dutch variables are not a good idea, I'm not english native and I always avoid to use my own language based names in code even for code I don't share, you never know who will read your code in 2 days, 2 months, or 2 years).
globals: Try to avoid using global variables as much as possible. Declare only constant in the global scope : const int foo = 1; or preprocessor defines #define foo 1, so that you do not spend too much of the little RAM space you have on your arduino. The only exception to that rule, which is very specific to arduino development, are objects (like Servo in your code) that you need to declare globally so you can set them up in the setup() function, and use them in the loop() function.
If I extrapolate on what you've written, you may want to add a handle_steering() function such as:
void handle_steering(int steering) {
if (steering > NN)
turn_left(steering);
elif (steering < NN)
turn_right(steering);
else
keep_straight();
}
and change the loop() to :
void loop() {
int throttle = read_throttle();
int steering = read_steering();
delay(5);
handle_throttle(throttle);
handle_steering(steering);
}
To make it a more general case of handling dynamically and flexibly a number of features, you could keep a few arrays:
PIN[]: containing the pins,
DOM_MIN[]: containing the minimum of the features' domain (for the map),
DOM_MAX[]: containing the maximum of the features' domain (for the map),
BOUND_MIN[]: containing the minimum boundaries (for the handle_steering condition),
BOUND_MAX[]: containing the maximum boundaries (for the handle_steering condition),
ACTION[]: containing the pointers to functions.
and then your algorithm would look like:
int read_input(int i) {
int value = pulseIn(PIN[i], HIGH, 20000);
return map(value, 1000, 2000, DOM_MIN[i], DOM_MAX[i]);
}
int handle_action(int i, int value) {
if (value > BOUND_MIN[i])
*(ACTION[i])(value);
elif (value < BOUND_MAX[i])
*(ACTION[i])(value);
else
*(ACTION[i])(-1);
}
void loop() {
for (int i=0; i<NB_INPUTS; ++i) {
int value = read_input(i);
delay(5);
handle_action(i, value);
}
}
but as you're still not comfortable with arrays (and I assume pointers as well), I would not recommend going further that way for the time being. First do it simple and make it work and then you could try to factorize it following the idea I'm exposing here. But you're making a software that is embedded, where the RAM is rare and the processing power is low, while your code space is cheap. So it's one of the few occasions where you'd better want to make a more redundant code that will stay in program space, whereas you want to manipulate as little symbols in the RAM. That's why I'm not showing you how the arrays are being declared/defined, and how to handle function pointers, because I don't think that solution (where you were heading) is the right way to do what you want.
Always remember, simpler is better!
HTH

Resources