For our project, we chose to implement an LCD frame buffer using the AVR. The chip was to store the current state, display the stored state on the LCD as necessary, and accept external commands to modify the displayed state.
The basic design of our system was strongly driven by what we could achieve at lower levels. The display architecture, synchronization, and data transfer were all largely determined by the the available resources and the necessity for a reasonably fast display.
We chose to have the entire display routine performed by a single microprocessor in the main line loop. By doing this, we were able to refresh the display as close to the maximum possible rate, which is important in reducing the inherent flicker on the LCD. Unfortunately, this resulted in many inconvienient design issues. First of all, we needed to make use of an external SRAM in order to store the entire frame in memory. In order to maintain a decent frame rate, we needed to use the built-in memory addressing. As a result, from the onset we were short two full ports and two bits from PORTD.
The necessity of using part of PORTD for the memory access meant that we had to use PORTB to output the full 8 bits required by the LCD. But by doing this, we could no longer use the SPI as originally intended to transfer instructions. Instead, we need to multiplex the output lines to PORTB to transfer data. Ensuring that there are no drive fights on the busses required the use of a simple(in theory) two-wire asynchronous handshaking protocol between the display engine and the command issuing processor.
In the implementation of the test pattern display on the LCD, there were three major issues that had to be dealt with in order to get a functional configuration.
The first major issue was the lack of any meaningful timing information. The vendor neglected to include in the documentation a proper timing diagram for the LCD. This required a rather lengthy online search through the SHARP web site until documentation for a similar product was uncovered.
The second critical issue was the timing and maximizing the refresh rate of the circuit. In order to maintain a fast frame rate, we needed to maximize the refresh rate of the system. In order to do this, we built the system externally and used a seperate crystal oscillator at 8-14MHz to drive the microcontroller.
Even with the faster oscillator, the flicker was still pretty bad. In order to further reduce the flicker, the inner display loop was unrolled 10 times. This avoided the loop overhead initially involved, and increased the final display rate by about 50%.
The final major issue was the lack of port pins on the AVR. Accessing the external memory requires the use of PORTA, PORTC, and the high two bits of PORTD. This left only PORTB suitable to transfer a full byte of data on each clock cycle. Unfortunately, this also meant that the SPI was not available for inter-processor handshaking, which makes the later multi-processor handshaking much more complex.
The initial implementation proceeded in steps. The first step was to display a single value that was hard wired into the program. This allowed us to verify that the LCD was working correctly. From here, a rather simple extension was made that stored a hard coded value into a single memory location. Finally, we loaded the actual pattern into memory and walked through the memory in the display routine. At this point, we could reliably display the memory pattern on the LCD.
The second step was an attempt to implement the inter-processor communication for the instruction set. Initially, we had intended to put the display routine in an ISR and perform instruction decoding in the main-line loop. While this scheme had many advantages, there was one big disadvantage. This was that coupling became a significant problem in our design. Attempting to drive a breadboarded system at 2-4MHz leads to some very interesting coupling problems. Basically, if you walked too close, the system would not function properly. Obviously, this was not acceptable as a solution.
To combat this problem, we moved the display routine into the main program. By avoiding the need to distribute high-frequency signals that are crucial to the functioning of the design, we significantly improved the system reliability. However, this requires a different scheme for processing instructions.
Instruction transfer is accomplished through use of the external interrupt together with a two-wire handshaking protocol.
Now that we had an interface into the actual buffer and refresh processor, we attempted to add a sinple instruction set that would allow us to perform all the functions we initially planned for. Our initial attempt at this involved taking in 8 bits in parallel over a multiplexed port. Our instructions ranged in length from 1 byte and 4 bytes. 1 byte instructions were used for general functions such as flipFrame and clearFrame. The 4 byte instructions were needed to specify an address in the display memory to be written. We decided to only implement access by memory address because we did not want to burden the screen refreashing processor with the calculations needed for a xy-coordinate reference. The memory access functions would take in 1 byte of opcode, 2 bytes of address and 1 byte for a value. Depending on the opcode, the 1 byte value would either be explicity written into the location or be used as a mask for an and/or/not/xor function on the referenced location in display memory.
The instruction set implemented on the buffer/refresh processor was never meant for use by the end user programmer. We would implement a much nicer interface for them by using a second processor to compute the necessary translation from xy-coordinate space to display memory location and then translate that into the buffer/refresh processor instruction set. For the sake of testability, we decided to use the UART as the user end interface to the secondary processor. Although the UART would not be able to transfer enough data to refreash the LCD at a reasonable animation quality, we decided that it would be good intermediate goal.
upon implementation, we ran into multiple issues with our proprietary parallel interface. Since we were running out of time fast, we decided to try to implement a smaller and simpler instruction set directly on the buffer/refresh processor.
although this project was not very code intensive, we believe that it is just barely out of the AVRs performance range. Mainly, we ran into almost all the physical limitations of the AVR. Our design would bave been much simpler if we just had more:
Perhaps this project would be better done using one of the AVRmega chips.