Barcode Sampling

January 1, 2009 | John Dybowski

Scanning
There are a number of parameters that must be considered when specifying the optical characteristics of a barcode scanner. Briefly, the optical wavelength can be centered in the visible red or infra-red spectrum. The advantage of using the visible spectrum is that if the barcode label looks okay to you it should appear likewise to the scanner.

Figure 1 - The X Dimension

The other thing you should be concerned with is the scanner's optical aperture size. A small spot size responds accurately to bar edges, but also picks up small spots and voids. Conversely, if the spot size is larger than the smallest bar/space element (the X dimension) then the scanner will have difficulty resolving the pattern. An aperture size about .8X works well for most codes. Standardization limits the number of choices to either high resolution (6 mil) or low resolution (10 mil).

Listing 1 - Barcode sample/decode structure
struct BC_STRUCT{ unsigned short sample_buffer[300]; unsigned short sample_ptr; unsigned short sample_ctr; unsigned char overflow_ctr; unsigned char sample_state; #define WAITING 0 #define BUSY 1 #define READY 2 unsigned char decode_buffer[48]; unsigned char decode_count; };

Sampling
To decode a barcode you need to collect timing information for all the bar/space elements of the symbol. To do this the first step is to scan the barcode, either physically (moving barcode or moving scanner) or electrically (CCD). As the barcode is being scanned the scanner converts the optical pattern into an electrical pattern, which is fed into a processor. The processor measures the width of the individual bars and spaces and stores the resulting counts in a buffer for processing by the decode routine.

You also need a means of determining when sampling is complete, which is accomplished with an overflow timer. Every time a sample is received, the sample routine re-arms the timer. When the scanner stops delivering sample data the timer is no longer refreshed and eventually overflows signaling that sampling has concluded.

The very simplest sampling implementation requires the processor to suspend all other operations and dedicate itself exclusively to the task of sampling barcode data. The actual timing measurements can be performed using a rudimentary counting loop in software. If the system has a hardware timer, it can be leveraging in lieu of the counting loop, usually providing better accuracy and greater resolution.

Most applications will sample under interrupts, utilizing a hardware timer. You must provide a means of generating an interrupt on each bar/space transition which might involve nothing more than properly configuring the interrupt controller or may require additional external circuitry, depending on the processor. Every time an interrupt occurs, the timer is stopped, read, and re-loaded. For this approach to work the processor must possess a fast, deterministic interrupt controller since the accuracy of the measurements depend on the interrupt response time and, more importantly, on the variations in interrupt response time.

Better results can be obtained with the aid of a timer capture system. In such a system the measurement count is copied into a capture register from a free-running timer without stopping the timer. This happens automatically in hardware under control of a pin that can also be used to assert an interrupt when the capture occurs. This decouples the measurement accuracy from the interrupt system performance since the processor has until the next transition to read the captured count before it's overwritten. Very accurate timing measurements can be achieved this way and it's easy to support multiple barcode channels simultaneously using this method.

Listing 2 - General Purpose Barcode Sampling
// Sampling void barcodeSample(struct bc_struct *p, unsigned int count){ if(p->sample_state == READY){ return; } p->overflow_ctr = BARCODE_OVERFLOW_TIME; if(p->sample_state != BUSY){ p->sample_ctr = 0; p->sample_ptr = p->sample_buffer; p->sample_state = BUSY; return; } if(p->sample_ctr++ >= (sizeof p->sample_buffer / 2) - 1){ p->sample_ctr = 0; p->overflow_ctr = 1; return; } *p->sample_ptr++ = count; } // Sample overflow void barcodeOverflow(struct bc_struct *p){ *p->sample_ptr++ = 0; p->sample_state = READY; }

Sampling Routine
Listing 2 is a hardware-independent sample routine that can support multiple barcode scanners. The input arguments include a pointer to a barcode structure and the sample count itself; the calling function deals with the underlying hardware that generates the count. The routine maintains the sample state using the sample_state member of the barcode structure. The routine first checks that sample_state is not READY, indicating a set of sample buffer is available and hasn't been processed yet, reloads the overflow timer, then looks at sample_state to determine if this is the first sample or if sampling is already in progress. If it's the first sample then the next few lines initialize the sample counter and pointer and set sample_state to BUSY indicating that the first sample has been processed. If sample_state is BUSY, indicating that sampling is already in progress, the routine store the count if the sample buffer has enough space.

Sample overflow is handled by the overflow routine, which appends a NUL count to the sample buffer to denote the end of the colleted samples and sets sample_state to READY. The READY sample_state has the dual function of informing the process code that it can attempt to decode the sample buffer and also prevents the sample routine from collecting any additional data, which would corrupt the buffer. The process code clears the READY sample_state once it's done processing the sample buffer, freeing the sample routine to to collect new sample data. The overflow routine can be invoked from some general time keeping code or from a timer interrupt.

One other thing to keep in mind is that most barcode scanners exhibit a timing distortion on the first bar since it takes a finite amount of time for the scanner's analog wave shaper to establish its internal reference. Generally, the distortion affects the first several samples but the problem is most pronounced on the first. Depending on the scanner it may be necessary to reconstruct the first sample. The simplest tactic is to replace the first sample with what the particular decode routine is expecting for a given symbology; either a narrow or a wide. There are more sophisticated ways to discern the value of the first sample but they require a knowledge of the specific electrical performance characteristics of the scanner. Also, the absolute scan velocity might have an impact on the amount of distortion.