Showing posts with label Embedded System. Show all posts
Showing posts with label Embedded System. Show all posts

Improving the stability of a crystal oscillator


• If you want a general crystal-controlled embedded system to
keep accurate time, you can choose to keep the device in an
oven (or fridge) at a fixed temperature, and fine-tune the

software to keep accurate time. This is, however, rarely
practical.

• ‘Temperature Compensated Crystal Oscillators’ (TCXOs)
are available that provide - in an easy-to-use package - a
crystal oscillator, and circuitry that compensates for changes
in temperature. Such devices provide stability levels of up to
±0.1 ppm (or more): in a clock circuit, this should gain or
lose no more than around 1 minute every 20 years.
TCXOs can cost in excess of $100.00 per unit...

• One practical alternative is to determine the temperature frequency
characteristics for your chosen crystal, and include
this information in your application.
For the cost of a small temperature sensor (around $2.00),
you can keep track of the temperature and adjust the timing
as required.

Reading and writing individual port pins.


/*-------------------------------------------------------------*-
Reading and writing individual port pins.
NOTE: Both pins on the same port
-*-------------------------------------------------------------*/
#include <reg52.H>
void Write_Bit_P1(const unsigned char, const bit);

bit Read_Bit_P1(const unsigned char);
/* ............................................................... */
void main (void)
{
bit x;
while(1)
{
x = Read_Bit_P1(0); /* Read Port 1, Pin 0 */
Write_Bit_P1(1,x); /* Write to Port 1, Pin 1 */
}
}
/* --------------------------------------------------------------- */
void Write_Bit_P1(const unsigned char PIN, const bit VALUE)
{
unsigned char p = 0x01; /* 00000001 */
/* Left shift appropriate number of places */
p <<= PIN;
/* If we want 1 output at this pin */
if (VALUE == 1)
{
P1 |= p; /* Bitwise OR */
return;
}
/* If we want 0 output at this pin */
p = ~p; /* Complement */
P1 &= p; /* Bitwise AND */
}

Receive SMS and turn on and off the devices


void main ()
{

clear();
lcdinit();
lcdcmd(0x80);
lcddata('K');

delay(10);
init();

SMSString("AT\r"); // AT commands to initialize gsm modem
delay(50);

SMSString( "ATe0\r"); // turn off echo
delay(50);

SMSString( "AT&W\r"); // save settings
delay(50);

SMSString( "AT+CMGF=1\r"); // select text mode for sms
delay(50);
SMSString( "AT+CNMI=2,1,0,0,0\r"); // notification of new sms
delay(10);

//New msg indication.
// +CMTI: "SM",3
while(1)
{
clear();
IE=0X90;   // Enable serial interrupt
delay(10);
read_notification(msg1);
IE=0x00;
}
}


void read_notification(unsigned char *msg)
{
unsigned char array[15],i,index_no;
i=0;
do
{
       msg++;
        i++;
}while(*msg!='+' && i!=15); // to check for first '+'.
i=0;

do
{
     array[i]=*msg++;
     i++;
}while(*msg!='\n' && i!=15);      // Check for new line.

if(strncmp(array, "+CMTI", 5) == 0)
{
        array[14] = '\0';
        IE=0x00;
        lcdcmd(0x80);
lcdstr("NEW MSG");// Just for indication.
        delay(50);
        SMSString("AT+CMGR=");
        tx0(array[12]);
        SMSString("\r");
        IE=0x90;
        delay(80);
        read_text(msg1,rec_no,time_date);
        delay(100);
}
return;
}

How to send SMS using AT89c51

#include <REGX51.H>
#include <AT89X51.H>

unsigned char *command_AT = "AT\r";
unsigned char *command_CMGF = "AT+CMGF=1\r";

unsigned char *command_CMGS = "AT+CMGS=\"+910123456789\"\r";
unsigned char *message = "Hello Dear";
unsigned char CTRLZ = 0x1A;

void puts(unsigned char* ptr);
void putc(unsigned char chr);
void sendsms(void);
void initialize();

main()
{
initialize();
sendsms();
while(1);
}

void initialize()
{
SCON = 0x50; /*SCON: mode 1, 8-bit UART, enable receive */
TMOD |= 0x20; /*TMOD: timer 1, mode 2, 8-bit */
TH1 = 0xFD; /*TH1: for 9600 baud */
TR1 = 1; /*TR1: timer 1 run */
}

void sendsms()
{
puts(command_AT);
puts(command_CMGF);
puts(command_CMGS);
puts(message);
putc(CTRLZ);
}

void puts(char* p)
{
char *temp = p; /*temp pointer so that the actual pointer is not displaced */
while(*temp != 0x00)
{
putc(*temp);
temp++;
}
}

void putc(unsigned char chr)
{
SBUF = chr;
while(TI==0); /*Wait until the character is completely sent */
TI=0; /*Reset the flag */

Word Length of a Microprocessor

Word Length of a Microprocessor is given as n-bit, where n may be 2,16,32 or 64. A binary digit 0 or 1 is called a bit. An 8 bit microprocessor can process 8 bit data at a time. If data consists of more than 8 bits, the          

processor takes up one by one for processing. Its ALU is designed to process 8 bit data. Its general purpose registers which hold data for processing, are 8 bit registers. Similarly, a 16 processor handles 16 bit of data at a time, its ALU processes 16 bit data and general purpose registers hold 16 bit data. Similarly,32 bit and 64 bit processors process 32 and 64 bit data at a time respectively. A processor of longer word length is more powerful and can process data at faster speed as compared with to a processor of shorter word length.

Why use the Project Header?


Use of PROJECT HEADER can help to make your code more
readable, not least because anyone using your projects knows where

to find key information, such as the model of microcontroller and
the oscillator frequency required to execute the software.

The use of a project header can help to make your code more easily
portable, by placing some of the key microcontroller-dependent data
in one place: if you change the processor or the oscillator used then
- in many cases - you will need to make changes only to the Project
Header.

Main.H



/*-------------------------------------------------------------*-
Main.H (v1.00)
-*-------------------------------------------------------------*/
#ifndef _MAIN_H
#define _MAIN_H
/*--------------------------------------------------------
WILL NEED TO EDIT THIS SECTION FOR EVERY PROJECT
-------------------------------------------------------- */
/* Must include the appropriate microcontroller header file here */
#include <reg52.h>
/* Oscillator / resonator frequency (in Hz) e.g. (11059200UL) */
#define OSC_FREQ (12000000UL)
/* Number of oscillations per instruction (12, etc)
12 - Original 8051 / 8052 and numerous modern versions
6 - Various Infineon and Philips devices, etc.
4 - Dallas 320, 520 etc.
1 - Dallas 420, etc. */
#define OSC_PER_INST (12)
/* --------------------------------------------------------
SHOULD NOT NEED TO EDIT THE SECTIONS BELOW
-------------------------------------------------------- */
/* Typedefs  */
typedef unsigned char tByte;
typedef unsigned int tWord;
typedef unsigned long tLong;
/* Interrupts  */
#define INTERRUPT_Timer_0_Overflow 1
#define INTERRUPT_Timer_1_Overflow 3
#define INTERRUPT_Timer_2_Overflow 5
#endif
/*-------------------------------------------------------------*-
---- END OF FILE ---------------------------------------
-*-------------------------------------------------------------*/

The Project Header


Project Header (Main.H)







#include <AT89S53.H>
...
#define OSC_FREQ (11059200UL)
...
typedef unsigned char tByte;


All program code in a single source file


It is possible to create ‘file-based-classes’ in C without imposing a
significant memory or CPU load.


Object-Oriented Programming with C



Adding Structure to Your Code


We will do three things

1. We will describe how to use an object-oriented style of
programming with C programs, allowing the creation of


libraries of code that can be easily adapted for use in different
embedded projects;

2. We will describe how to create and use a ‘Project Header’
file. This file encapsulates key aspects of the hardware
environment, such as the type of processor to be used, the
oscillator frequency and the number of oscillator cycles
required to execute each instruction. This helps to document
the system, and makes it easier to port the code to a different
processor.

3. We will describe how to create and use a ‘Port Header’ file.
This brings together all details of the port access from the
whole system. Like the Project Header, this helps during
porting and also serves as a means of documenting important
system features.

Example: Counting goats


• With the simple code in the previous example, problems can
arise whenever a switch is pressed for a period longer than
the debounce interval.
• This is a concern, because in many cases, users will press
switches for at least 500 ms (or until they receive feedback
that the system has detected the switch press). As a result, a


user typing “Hello” on a keypad may see:
“HHHHHHHHHeeeeeeeeellllllllllllllllooooooooooo”
appear on the screen.


One consequence is that this code is not suitable for applications
where we need to count the number of times that a switch is pressed
and then released.




/*-------------------------------------------------------------*-
A 'goat counting' program for the 8051...
-*-------------------------------------------------------------*/
#include <Reg52.h>
/* Connect switch to this pin */
sbit Switch_pin = P1^0;
/* Display count (binary) on this port */
#define Count_port P3
/* Return values from Switch_Get_Input() */
#define SWITCH_NOT_PRESSED (bit) 0
#define SWITCH_PRESSED (bit) 1
/* Function prototypes */
void SWITCH_Init(void);
bit SWITCH_Get_Input(const unsigned char DEBOUNCE_PERIOD);
void DISPLAY_COUNT_Init(void);
void DISPLAY_COUNT_Update(const unsigned char);
void DELAY_LOOP_Wait(const unsigned int DELAY_MS);
/* ---------------------------------------------------------------- */
void main(void)
{
unsigned char Switch_presses = 0;
/* Init functions */
SWITCH_Init();
DISPLAY_COUNT_Init();
while(1)
{
if (SWITCH_Get_Input(30) == SWITCH_PRESSED)
{
Switch_presses++;
}
DISPLAY_COUNT_Update(Switch_presses);
}
}

/*-------------------------------------------------------------*/
void SWITCH_Init(void)
{
Switch_pin = 1; /* Use this pin for input */
}
/*-------------------------------------------------------------*-
SWITCH_Get_Input()
Reads and debounces a mechanical switch as follows:
1. If switch is not pressed, return SWITCH_NOT_PRESSED.
2. If switch is pressed, wait for the DEBOUNCE_PERIOD (in ms).
Then:
a. If switch is no longer pressed, return SWITCH_NOT_PRESSED.
b. If switch is still pressed, wait (indefinitely) for
switch to be released, *then* return SWITCH_PRESSED
See Switch_Wait.H for details of return values.
-*-------------------------------------------------------------*/
bit SWITCH_Get_Input(const unsigned char DEBOUNCE_PERIOD)
{
bit Return_value = SWITCH_NOT_PRESSED;
if (Switch_pin == 0)
{
/* Switch is pressed */
/* Debounce - just wait... */
DELAY_LOOP_Wait(DEBOUNCE_PERIOD);
/* Check switch again */
if (Switch_pin == 0)
{
/* Wait until the switch is released. */
while (Switch_pin == 0);
Return_value = SWITCH_PRESSED;
}
}
/* Now (finally) return switch value */
return Return_value;
}

/*-------------------------------------------------------------*-
DISPLAY_COUNT_Init()
Initialisation function for the DISPLAY COUNT library.
-*-------------------------------------------------------------*/
void DISPLAY_COUNT_Init(void)
{
Count_port = 0x00;
}
/*-------------------------------------------------------------*-
DISPLAY_COUNT_Update()
Simple function to display tByte data (COUNT)
on LEDs connected to port (Count_Port)
-*-------------------------------------------------------------*/
void DISPLAY_COUNT_Update(const unsigned char COUNT)
{
Count_port = COUNT;
}
/*-------------------------------------------------------------*-
DELAY_LOOP_Wait()
Delay duration varies with parameter.
Parameter is, *ROUGHLY*, the delay, in milliseconds,
on 12MHz 8051 (12 osc cycles).
You need to adjust the timing for your application!
-*-------------------------------------------------------------*/
void DELAY_LOOP_Wait(const unsigned int DELAY_MS)
{
unsigned int x, y;
for (x = 0; x <= DELAY_MS; x++)
{
for (y = 0; y <= 120; y++);
}
}


Conclusions
The switch interface code presented and discussed in this seminar
has allowed us to do two things:
• To perform an activity while a switch is depressed;
• To respond to the fact that a user has pressed – and then
released – a switch.
In both cases, we have illustrated how the switch may be
‘debounced’ in software.











Example: Reading switch inputs (basic code)


This switch-reading code is adequate if we want to perform
operations such as:
• Drive a motor while a switch is pressed.
• Switch on a light while a switch is pressed.


• Activate a pump while a switch is pressed.
These operations could be implemented using an electrical switch,
without using a microcontroller; however, use of a microcontroller
may well be appropriate if we require more complex behaviour. For
example:
• Drive a motor while a switch is pressed
Condition: If the safety guard is not in place, don’t turn the
motor. Instead sound a buzzer for 2 seconds.
• Switch on a light while a switch is pressed
Condition: To save power, ignore requests to turn on the
light during daylight hours.
• Activate a pump while a switch is pressed
Condition: If the main water reservoir is below 300 litres, do
not start the main pump: instead, start the reserve pump and
draw the water from the emergency tank.

/*-------------------------------------------------------------*-
Switch_read.C (v1.00)
--------------------------------------------------------
A simple 'switch input' program for the 8051.
- Reads (and debounces) switch input on Pin 1^0
- If switch is pressed, changes Port 3 output
-*-------------------------------------------------------------*/
#include <Reg52.h>
/* Connect switch to this pin */
sbit Switch_pin = P1^0;
/* Display switch status on this port */
#define Output_port P3
/* Return values from Switch_Get_Input() */
#define SWITCH_NOT_PRESSED (bit) 0
#define SWITCH_PRESSED (bit) 1
/* Function prototypes */
void SWITCH_Init(void);
bit SWITCH_Get_Input(const unsigned char DEBOUNCE_PERIOD);
void DISPLAY_SWITCH_STATUS_Init(void);
void DISPLAY_SWITCH_STATUS_Update(const bit);
void DELAY_LOOP_Wait(const unsigned int DELAY_MS);

/* ---------------------------------------------------------------- */
void main(void)
{
bit Sw_state;
/* Init functions */
SWITCH_Init();
DISPLAY_SWITCH_STATUS_Init();
while(1)
{
Sw_state = SWITCH_Get_Input(30);
DISPLAY_SWITCH_STATUS_Update(Sw_state);
}
}
/*-------------------------------------------------------------*-
SWITCH_Init()
Initialisation function for the switch library.
-*-------------------------------------------------------------*/
void SWITCH_Init(void)
{
Switch_pin = 1; /* Use this pin for input */
}
/*-------------------------------------------------------------*-
SWITCH_Get_Input()
Reads and debounces a mechanical switch as follows:
1. If switch is not pressed, return SWITCH_NOT_PRESSED.
2. If switch is pressed, wait for the DEBOUNCE_PERIOD (in ms).
Then:
a. If switch is no longer pressed, return SWITCH_NOT_PRESSED.
b. If switch is still pressed, return SWITCH_PRESSED
See Switch_Wait.H for details of return values.
-*-------------------------------------------------------------*/
bit SWITCH_Get_Input(const unsigned char DEBOUNCE_PERIOD)
{
bit Return_value = SWITCH_NOT_PRESSED;
if (Switch_pin == 0)
{
/* Switch is pressed */
/* Debounce - just wait... */
DELAY_LOOP_Wait(DEBOUNCE_PERIOD);
/* Check switch again */
if (Switch_pin == 0)
{
Return_value = SWITCH_PRESSED;
}
}
/* Now return switch value */
return Return_value;
}
/*-------------------------------------------------------------*-
DISPLAY_SWITCH_STATUS_Init()
Initialization function for the DISPLAY_SWITCH_STATUS library.
-*-------------------------------------------------------------*/
void DISPLAY_SWITCH_STATUS_Init(void)
{
Output_port = 0xF0;
}
/*-------------------------------------------------------------*-
DISPLAY_SWITCH_STATUS_Update()
Simple function to display data (SWITCH_STATUS)
on LEDs connected to port (Output_Port)
-*-------------------------------------------------------------*/
void DISPLAY_SWITCH_STATUS_Update(const bit SWITCH_STATUS)
{
if (SWITCH_STATUS == SWITCH_PRESSED)
{
Output_port = 0x0F;
}
else
{
Output_port = 0xF0;
}
}
/*-------------------------------------------------------------*-
DELAY_LOOP_Wait()
Delay duration varies with parameter.
Parameter is, *ROUGHLY*, the delay, in milliseconds,
on 12MHz 8051 (12 osc cycles).
You need to adjust the timing for your application!
-*-------------------------------------------------------------*/
void DELAY_LOOP_Wait(const unsigned int DELAY_MS)
{
unsigned int x, y;
for (x = 0; x <= DELAY_MS; x++)
{
for (y = 0; y <= 120; y++);
}
}


Dealing with switch bounce


In practice, all mechanical switch contacts bounce (that is, turn on
and off, repeatedly, for a short period of time) after the switch is
closed or opened.


As far as the microcontroller is concerned, each ‘bounce’ is


equivalent to one press and release of an ‘ideal’ switch. Without
appropriate software design, this can give rise to a number of
problems, not least:
• Rather than reading ‘A’ from a keypad, we may read
‘AAAAA’
• Counting the number of times that a switch is pressed
becomes extremely difficult.
• If a switch is depressed once, and then released some time
later, the ‘bounce’ may make it appear as if the switch has
been pressed again (at the time of release).

Creating some simple software to check for a valid switch input is
straightforward:
1. We read the relevant port pin.
2. If we think we have detected a switch depression, we wait for
20 ms and then read the pin again.
3. If the second reading confirms the first reading, we assume
the switch really has been depressed.
Note that the figure of ‘20 ms’ will, of course, depend on the switch
used.

The need for pull-up resistors




This hardware operates as follows:
• When the switch is open, it has no impact on the port pin.
An internal resistor on the port ‘pulls up’ the pin to the


supply voltage of the microcontroller (typically 5V). If we
read the pin, we will see the value ‘1’.
• When the switch is closed (pressed), the pin voltage will be
0V. If we read the the pin, we will see the value ‘0’.




Example: Reading and writing bits (generic version)

The six bitwise operators:



/* Desktop program - illustrating the use of bitwise operators */
#include <stdio.h>
void Display_Byte(const unsigned char);
/* ............................................................... */


int main()
{
unsigned char x = 0xFE;
unsigned int y = 0x0A0B;
printf("%-35s","x");
Display_Byte(x);
printf("%-35s","1s complement [~x]");
Display_Byte(~x);
printf("%-35s","Bitwise AND [x & 0x0f]");
Display_Byte(x & 0x0f);
printf("%-35s","Bitwise OR [x | 0x0f]");
Display_Byte(x | 0x0f);
printf("%-35s","Bitwise XOR [x ^ 0x0f]");
Display_Byte(x ^ 0x0f);
printf("%-35s","Left shift, 1 place [x <<= 1] ");
Display_Byte(x <<= 1);
x = 0xfe; /* Return x to original value */
printf("%-35s","Right shift, 4 places [x >>= 4]");
Display_Byte(x >>= 4);
printf("\n\n");
printf("%-35s","Display MS byte of unsigned int y");
Display_Byte((unsigned char) (y >> 8));
printf("%-35s","Display LS byte of unsigned int y");
Display_Byte((unsigned char) (y & 0xFF));
return 0;
}
COPYRIGHT ©MICHAEL J. PONT, 2001-2006. Contains material from:
Pont, M.J. (2002) “Embedded C”, Addison-Wesley.
PES I - 53
/* --------------------------------------------------------------- */
void Display_Byte(const unsigned char CH)
{
unsigned char i, c = CH;
unsigned char Mask = 1 << 7;
for (i = 1; i <= 8; i++)
{
putchar(c & Mask ? '1' : '0');
c <<= 1;
}
putchar('\n');
}


Experienced ‘C’ programmers please note these lines:


sbit Switch_pin = P1^0;
sbit LED_pin = P1^1;
Here we gain access to two port pins through the use of an sbit
variable declaration. The symbol ‘^’ is used, but the XOR bitwise
operator is NOT involved.

Example: Reading and writing bits (simple version)


-*-------------------------------------------------------------*/
#include <Reg52.H>
sbit Switch_pin = P1^0;
sbit LED_pin = P1^1;
/* ............................................................... */
void main (void)


{
bit x;
/* Set switch pin for reading */
Switch_pin = 1;
while(1)
{
x = Switch_pin; /* Read Pin 1.0 */
LED_pin = x; /* Write to Pin 1.1 */
}
}
/*-------------------------------------------------------------*-
---- END OF FILE ---------------------------------------
-*-------------------------------------------------------------*/

Example: Reading and writing bytes (review)


void main (void)
{
unsigned char Port1_value;
/* Must set up P1 for reading */


P1 = 0xFF;
while(1)
{
/* Read the value of P1 */
Port1_value = P1;
/* Copy the value to P2 */
P2 = Port1_value;
}
}