computer interfacing tutorial-printer, serial, game, usb port
 

 

Single-Wire Sensors Interface Directly To A Parallel Port PC using PWM technique

Over the years, various vendors have developed a number of interfacing techniques to help address design requirements for simplified signal conditioning, reduced component count, and lowered development and system costs. One such low-cost interfacing technique is the use of pulse-width-modulated (PWM) digital outputs to eliminate the need for costly analog-to-digital converters (ADCs). This approach also decreases the number of signal wires required.

Using this PWM approach, the measured data's value is passed on as a variable-length pulse to the measurement and control system through a single-wire interface. The on-period (tON) of the pulse indicates the value of the measured quantity. Any change in the measured value will cause tON to vary. The measured data can be captured to a specified degree of resolution by measuring tON. Modern microcontrollers have an onboard timer/counter peripheral that can easily be used to measure the pulse width.

PWM-output devices (single-wire interface) can be interfaced directly to a PC as shown in the figure. In a PC, there are no unused timer/counters available for measuring pulse widths. However, the PC's counter2 (a 16-bit, 8254 timer/counter), normally utilized for the PC's speaker operation, can be used for measuring the pulse width of a PWM signal. This counter operates using a clock frequency of 1.1931817 MHz (TIMER_FREQUENCY) and can be enabled or disabled by setting bit-0 of port 61h to one or zero, respectively.

For this application, counter2 is operated in mode 2 (i.e., as rate generator) and is configured by loading its control register at port 043h with a control word value of 0b4h. Initially, counter2 is loaded with an ffffh count at port 042h (i.e., counter2's read/write I/O port) in two cycles. During operation, the input signal is sampled continuously and, at the first rising edge, counter2 is enabled by setting bit-0 of port 61h to one. When counter2 is enabled, its count follows an auto-decrement pattern of one count every 0.838095 µs (1/TIMER_FREQUENCY). As long as the input pulse is high, counter2's count (elapsed_count) is read back in two cycles from port 042h. At this juncture of operation, the elapsed count is checked to see if the time period of the pulse is greater than ~54.9 ms (this is the maximum time period that counter2 can measure). If the input pulse width is greater than this limit, the overflow counter is incremented and the counter2 count is automatically set to an ffffh count. With the trailing edge of the input pulse, counter2 is disabled by setting bit-0 of port 61h to zero.

The counter data is stored in a data array (count[i]) whose length depends on the pulse width. If the pulse width is less then 1 ms, the length of the data array is 20; otherwise, it is 1. This is necessary to minimize the error when measuring small-pulse-width signals. The 20 array elements are arranged in ascending order (averagedata( )) and the average of the 10 middle values is used in the calculation of pulse width. Pulse width (pulse_width) is calculated using the overflow counter and the counter's count data. It is then displayed. Before starting the next read cycle, counter2 is reset with a count of ffffh. This process continues until the user presses the 'Q' key, which terminates the program and restores the screen attributes.

Click here to download the software listing, written in C (Turbo C++, version 3.0) and assembly. It was tested in the MS-DOS mode of Win95 on a 450-MHz Pentium II. The minimum pulse width that could be measured was ~23 µs. The software can be easily modified to determine tOFF or to calculate the frequency of the input pulse stream.

/***************************************************/
/* Single-Wire Sensors Interface Directly To A PC */
/* By: Darvinder Singh Oberoi and Harinder Dhingra */
/***************************************************/
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <math.h>
#pragma inline
#define TIMER_FREQUENCY 1193181.7 /* Counter2's operating frequency            */
#define PRINTER_PORT 0x378 /* Base Address of Printer Port */
#define READ_PORT PRINTER_PORT + 1 /* Input Pulse Port */
#define Resolution 7 /* ~54.9 ms limit Check Resolution Counts */
#define Delay_Count 4 /* Counting Delays due to iteration and checks*/
/* Resolution and Delay_Count values can be changed to get the most precise reading */
/* Resolution variable is required for checking the new and old reading boundary conditions */
/* Delay variable takes care of delays due to iteration cycles, useful for error correction at small pulse width signal */
/* Both these values should be set to minimum */
/* Minimum input pulse width is approx. 0.023 ms */
float averagedata( unsigned data[20], unsigned data_size ); /* Average of data samples */
void screen_display( ); /* Initial Set Up Screen */
main( ) /* Program Starts Here */
{
           char ch;
           unsigned count[20],i,prev_count,elapsed_count ;
           float pulse_width, overflow;
           screen_display();
           asm mov al, 0b4h /* Set Counter2 mode to 2 */
           asm out 043h,al /* 8254's control Port address i.e. 43h */
/*---- Start the read Cycle Here ----*/
           for( ;ch != 'q'; ) /* Endless Loop until 'Q' is pressed */
           {
           i=0;
           for (;;)
           {
           asm mov dx, READ_PORT /* Set the input signal port */
           asm mov al, 0ffh
           asm out 042h, al /* Load the initial count in two cycles */
           asm out 042h, al /* Counter2 Port address i.e. 42h */
           prev_count = 65535; /* Initialization */
           overflow = 0.0;
           /* Check for the first rising edge - i.e. first transition from 0 to            1 */
           /* Input Signal is at pin no:13 */
           reading_1:
           asm in ax, dx
           asm and ax, 10h
           asm jnz reading_1
           reading_0:
           asm in ax, dx
           asm and ax, 10h
           asm jz reading_0
 /* Enable Counter2 */
           asm mov al, 01h /* Set enable bit */
           asm out 061h, al
 /* Check for High level input signal */
           still_reading_1:
           asm in al, 042h /* Read the contents of counter2*/
           asm mov bl, al
           asm in al, 042h
           asm mov bh, al
           asm mov elapsed_count, bx
           /* Check if pulse width is greater than multiples of ~54.9 mS */
           /* If pulse is greater, then increment the overflow flag */
           if( ( prev_count <= Resolution) && (elapsed_count >= (65536- Resolution) ) )
           ++overflow;
           prev_count = elapsed_count;
           asm in ax, dx
           asm and ax, 10h
           asm jnz still_reading_1
           count[ i ] = elapsed_count; /* Store data in array */
 /* Disable the counter as soon as 1-to-0 transistion is detected */
           asm mov al, 0h /* Reset the enable bit to 0 */
           asm out 061h, al /* Disable counter */
 /* If pulse width is less than 1 mS */
           if ( elapsed_count >= 64343 && overflow == 0 )
           {
           ++i;
           if ( i > 19 ) break;
           }
           else /* Pulse width greater than 1mS */
           {
           i = 1;
           break;
           }
           }
/* Start Calculations, get the average data */
pulse_width = (overflow * 65536.0+(float)(0xffff - averagedata(count,i-1)+Delay_Count)) * 1000 /TIMER_FREQUENCY ;
 gotoxy( 35,11 ); printf("%3.5f ", pulse_width);
gotoxy( 35,11 );
delay( 20 ); /* Dummy Delay */
if( kbhit( ) ) /* Check for Quit keys */
ch = getch( );
if( ch == 'q' || ch == 'Q' ) ch ='q';
 }
           textcolor( LIGHTGRAY ); /* Restore screen attributes */
           clrscr( );
           } /*--- End of the MAIN Program ---*/
/* Arrange the data in ascending order and select 10 middle entries            */
           float averagedata( unsigned data[ ], unsigned data_size )
           {
           float total_sum = 0;
           unsigned temp;
           int ii,j;
/* Arrange data in ascending order */
           if ( data_size > 1 )
           {
           for( ii=0; ii <= data_size; ii++ )
           {
           for( j=0; j <= data_size - 1; j++ )
           {
           if( data[ j ] <= data[ j + 1 ] )
           {
           temp = data[ j ];
           data[ j ]= data[ j + 1 ];
           data[ j + 1 ] = temp;
           }
           }
           }
           /* Get 10 middle values and find the average */
           for( ii=5; ii <= 14; ii++ )
           total_sum = total_sum + (float) data[ ii ];
           total_sum /= 10.0;
           return total_sum;
           }
           else
           return data[ 0 ];
           }
/* Initial screen setup */
           void screen_display( )
           {
           clrscr( );
           textcolor( YELLOW );
           gotoxy( 25, 2 ); cprintf("Pulse Width Measurement");
           gotoxy( 5, 11 );
           textcolor( YELLOW );
           gotoxy( 30, 10 );
           cprintf( "Pulse Width (ms)" );
           gotoxy( 20, 22 ); cprintf( "< Press Q/q key to Quit the Program            > " );
           }

BACK

 

Free Software
Delphi

Lesson 1
Delphi Programming
1.1. IDE Delphi
1.2. Component


Lesson 2
Printer Port
/ LPT
1.1.Basic
1.2.Address
1.3.Port Register
1.4.8 Bit Data Input
1.5.Test Circuitry
1.6.Assignment

Lesson 3
Printer Port / LPT
Experiments

3.1.LED
3.2.Swicht
3.3.Motor Stepper
3.4.DAC
3.5.ADC
3.6.Graph Display

Lesson 4
Serial Port

4.1.Basic
4.2.Hardware
4.3.Port Register

Lesson 5
Serial Port Experiments

5.1.LED
5.2.Stepper Motor
5.3.Swicht
5.4.ADC

Lesson 6
Game Port
Joy Stick
6.1. Basic
6.2. Experiments