Two Digits Seven Segment Serial Display Peripheral Board for Pinguino
In this article I will show you the complete development and building from scratch of a peripheral board that will serve as a tiny display unit for Pinguino Boards. The aim of this project is to explain to students and hobbyists a working method that can lead from ideas to a real working device.
There are many “ready to use” peripherals available to buy or to assemble but you will never understand the real behaviour underneath a system if you don't get your hands dirty implementing it on your own.
Most Pinguino boards does not have displays so debug is not so easy as it is in a real PC environment. There are also some situations where you cannot simply add breakpoints to your code and view it in your IDE, because you cannot stop the program flow. Consider for example real time decoding of a data stream. You cannot stop it just to see what happens in a certain point of the code.
The easier thing to do is to put in the program flow some two digit code as a flag and pass it to an external device to display it. So you will know where you are in the program flow and what branch is being executed. Although this was the original idea behind this work, this device can also be used as a general two digit display, should you need it in your project.
In this paper we will see every step of design process: Project goals and constraints, Implementation ideas, hardware peripheral design, Host-Device protocol design, PCB layout design, Protocol Handshaking simulation, C Coding of the host communication routine, C Coding of the device routine. Measure and testing.
I will assume in the rest of the article that you have some basic knowledge of digital electronics, Java programming, C programming and that you are familiar with the Microchip MPLAB-X IDE and development environment and the Pinguino IDE.
Project Goals and Constraints
When I started this work and before writing anything I thought about the overall complexity and I fixed some project goals and some constraints.
1. The device has to be powered from the Pinguino Board so the power supply needs to adapt either the 3.3 or 5 volt environment.
2. We have to minimize Pinguino pin usage to free them for the real application to be developed, so communication with device has to be serial.
3. Device component count has to be as limited as possible to simplify the board layout and costs.
4. The device should be provided with a very light host protocol device driver written in C to allow device usage in a huge variety of platforms.
5. The user should not have to worry about timing and baud rates to simplify device usage, so the communication mechanism has to be speed adaptive. This is achieved by implementing communication in the form of a cooperative state machine between host and device.
6. Should something happen during a host-device transmission that stops the protocol flow, the device has to auto reset and so does the host.
1. Since I have many components in my lab I will use what I have around, but looking forward to an open architecture that allows for easy substitution should it be needed.
2. My home PCB manufacturing expertise doesn't allow me to work with tracks less than 20 mils [1 mil is 1/1000 of inch that is 0.0254 mm] so the PCB layout should be at maximum a double-sided board with 20 mils tracks or larger if possible.
3. The human eye is slow compared to any micro-controller so there is no need for a fast computing processor since most of the time the device will be in an idle state. There is not much sense to have a device capable of handling a huge amount of digits per second from the host if your brain-eye central processor needs at least 20 milliseconds to “understand” the digit shown.
Overall implementation ideas
The concept of this work is very simple in principle. There are two separate tasks running in the device.
The first task simply to read two variables, each holding a digit to be shown and using a multiplexing technique to activate the right display segments.
The second task listens to incoming data from the host serial interface and updates both variables. So each atomic transmission from the host consists of two bytes.
Until new data comes from the host, the first task will continue to show the digits stored, so the device can be seen as a sort of sample and hold 16 bit register. The two digits will be displaying data as if they were in parallel from an external observer.
The reason for using a multiplexing technique is to save data lines and therefore pins. Actually a unique 8 bit data bus is needed to activate both displays, so each display will be shown for a short period of time. We leave to our brain's slowness the perception that both digits are being displayed at the same time.
So, the first thing to do is to choose the multiplexing frequency to be used.
The human eye can perceive a sensory eye stimulus if it lasts longer than about 25-30 msec. If you remember the old movie industry most films are taped at 25 frames per second rate.
So each frame lasts for about 40 msec, giving the viewer the sense of continuous motion.
In our case to have the digit displayed rock solid and still, without any flickering, we will set the multiplexing speed to about 16 msec for each digit. This value is not critical in the sense that 15, 17 or 18 msec does not make any perceptible difference. The actual value will depend on the clock speed we will choose for the processor.
How to implement multiplexing
We now have to think about the method to implement the multiplexing function. Most microprocessor have available internal timers that are actually counters (8, 16, 32 bit) which are incremented either by an internal clock source or by an external trigger event. When the counter reaches a specific value that can be the maximum or a specific user chosen value, an interrupt signal is automatically generated raising a flag so that the processor will jump to a specific interrupt handler routine which executes some code and resets the interrupt flag. So we will create an interrupt handler that simply puts the right digit bit-code on the data bus for switching on the right LED segments.
Since we said that we want a multiplexing speed of 16 msec, the interrupt handler will display alternately the digit A and B. So each digit A update is 16 msec apart in time and hence the time between A and B has to be divided by 2. So the interrupt handler routine has to be called every 8 msec.
The multiplexed idea mechanism is shown in the next picture :
Here we find an 8 bit counter that increments from 0 to 255 in about 8 msec. When the counter passes 255, it rolls over to 0 and a timer overflow signal is generated. At time t1 the interrupt routine is fired and it is executed in (t2-t1) time putting the digit data on the bus. The interrupt routine remembers the last digit updated, so at the next interrupt the other digit's data will be used. This process repeats for ever.
Host-Device Protocol Concept
Host and Device will communicate with each other in a serial way, so the host will shift out 2x8 bits, one at a time and the device will fill two separate data variables storing the value to be displayed.
To implement serial communication we can follow three main conceptual methods.
We can use the SPI protocol communication bus that consists of three lines: Clock, MOSI (Master Out Slave Input) and MISO (Master Input Slave Output). The clock is generated by the host which plays the role of the master. This flow is full duplex in nature.
We can use the I2C protocol that uses only two lines SCL (clock) and SDA (data). The clock SCL is always generated by the host, but the data line (SDA) is used either for writing data or for reading, so both host and the device have to handle and harmonize the data direction flow. This flow is half duplex in nature
We can use the 1Wire protocol that uses only 1 Wire for transmitting both clock and data in both directions according to a very clever time pulse length schema. This flow is again half duplex in nature.
Although the most economical method is 1Wire protocol, the easiest to implement is SPI since we do not have to use any address device schema or bidirectional data line handling. So the first version of this firmware will use SPI protocol.
Actually we will use a variation of the SPI protocol to make it speed adaptive.
In the standard SPI protocol the host does know anything about device; it barely knows that the device exists at all. The host thinks in this way .. I set a clock speed lower than the maximum device allowable speed, I select it by lowering a Slave Select signal and for each conventional clock edge I put a bit out and read a bit in. If the device is unable to read for any reason it is not my problem.
With this schema many over-run or under-run errors may occur if the device is computing different threads in parallel and cannot respond within the right time for the clock edge signal.
The reason that led the SPI protocol designers to implement it this way is that in principle there is a physical shift register inside device that has the input clock pin under direct control of the clock's physical communication line. Using a microcontroller we instead implement this behavior in software by just reading the pin so a method to spoof exactly the shift register behavior is to tailor the clock edge to an interrupt signal and use an interrupt handler. Unfortunately the target processor I planned to use (as we will see in a next section) only allows an interrupt coming from port B so I chose a different approach.
The device will only receive data so we can use the SDO line (Slave Data Out) as an acknowledge line for the host. The host device protocol can be easily described as follows:
- Host MOSI line (master output) is connected to Device SDI (serial data input). - Host MISO line (master input) is connected to Device SDO (serial data output). - We set the idle clock state HIGH and the SDO line HIGH as well.
- The host signals its intention to transmit a bit by lowering the clock line and the slave, when ready, will reply by lowering the SDO line .
- At this point the host will put the bit to be transmitted on the MOSI line. When the host considers the MOSI line stable, it sets the clock HIGH, signalling to device "I've put valid data on the bus".
- The device when it finds the clock line HIGH, reads the MOSI line and updates its shift register. When finished, the device raises the SDO line signaling to the host "I'm done". While waiting for the SDO line to become HIGH, the host reads a 0 on the SDO line and update its input shift register.
- The same process repeats for each bit.
Host and device will wait for each other to be ready, so the data speed will adapt itself automatically and the two byte transfer is executed at the maximum speed possible for both. At the end of each successful byte transmission the host has read a 0 byte from device.
If the device or the host is in the middle of any transmission and one of the endpoints stops for any reason to respond properly, after a timeout the device will clear its shift register and reset itself to an idle state displaying the last two valid digits received.
The host state machine will give up too after a timeout, and will return an error to the calling process. So the transmission routine will be “non blocking”.
In this diagram we can see the process described:
So far we have completed the conceptual part of our work. If everything is clear, in the next section 7SDevice_part2 we will design the hardware board.