Solutions to exercise 13: Seven Segment display and Keypad
The Seven Segment Display and the TM1637 controller chip
Exercise 1: The driver and its test program
Writing a bit banging driver is a non-trivial task. Not only must you correctly set the signal levels but you also must get the timing of the signals right. All this must be done by programming GPIO pins. But how can you make sure the signals have the shape they should have as described in the chip's data sheet? The best way of course is to observe the signals on an oscilloscope. If you don't have a scope at hand then you can try to log the signals and their timing to files.
This is the strategy I opted for: Each time I set a signal to either the dio or the clk pin I write this event into a log file. as long as I wait, I copy the current signals levels onto the log file.
Before starting to write the actual driver I only try to transfer a byte, making sure the chip answers with the expected acknowledge signal, and plot the log file for control.
You can clearly the see sequence:
- first the start sequence: dio going from high to low while clk is high
- then the 8 data bits (here I send the value 0x55)
- reading the acknowledge signal on the nineth clock cycle
- and finally the stop sequence: dio going from low to high while clk is high
Please compare this plot with the timing diagrams from the
TM1637 data sheet.
The tricky bit about such a driver is that you are "walking in the dark" until you see the first segment light up. Once you have got this far you are usually not too far away from success.
The driver tm1637.py has the following methods:
- start_transfer: implements the start sequence of the protocol
- write_bit(bit): write a single bit, cannot be called separately but is used by write_byte
- write byte(data): write the bit data and check for the acknowledge from the tm1637 chip
- stop_transfer: implement the stop sequence
The above three methods are foreseen for internal use while the following calls are intended for use by applications:
- display_on: switch the display on.
- display_off: switch display off
- clear_digits(colon): blank the display. If colon is True, the colon is switched on
- write_segments(digit_num,segment_code,colon) write the segment code to the digit
- write_digit(digit_num,digit,colon): write the value digit to digit number digit_num. If digit_num is the second digit (digits are counted from zero) and color is true then the colon is also switched on
- write_hex(number,colon): write the 16 bit hex number number to the display. If colon is true then the colon will also be switched on
- write_dec(number,colon): same as write_hex but for decimal numbers.
- set brightness(level): level is 0..7. The brightness level is used for all consecutive writing to the display.
In addition to the driver itself, which must be uploaded to the ESP32 before use
ampy put tm1637.py /lib/tm1637.py
the test program itestTM1637.py s provided showing how to use the driver.
If you want to see the timing diagram you should set tm1637.debug to true. Like this each setting of the dio or clk line is recorded. At the end of the program you can write these data to files on the ESP32 file system and transfer them with ampy:
ampy get /data/dio.txt > dio.txt
ampy get /data/clk.txt > clk.txt
Have a look at testTM1637_debug.py to find out how to write these files. The /data directory must be prepared on the ESP32 before.
Exercise 2: A clock program
Once the driver is working it is easy to write programs using the seven segment display. As an example we implement a clock program. First we connect to WiFi using the wifi_connect module which also sets the ESP32 real time clock with the current date and time.
Then we create a TM1637 instance, switch the display on and set it with the current time. The
cetTime method of wifi_connect gets the current CET time (use gmtTime if you are in the GMT time zone).
Then we provide a callback function for timer interrupts which is triggered every second. The callback function again gets the current time and calls the write_dec method in the tm1637 driver to display the current time. The colon is toggled every second.
The Keypad
Exercise 1 and 2:
In order to scan the keypad I set all column pins to input with pull-up. In order to scan I set one of the row pins to output (keeping the other input with pull-up) and I pull this pin to ground. If a button in this row is pressed then a zero will be read on one of the column pins, if not, all column pins will read a high value.
Now cycle all row pins, switching only one of them to output with value zero and read every column pin.
Exercise 1 is implemented in keypad_V1, exercise 2 in keypad_V2.
Exercise 3:
A circular buffer is a data structure found very frequently in embedded systems. In addition to a fixed size buffer it has 2 pointers, one containing the index where the next element is to be written and one containing the index of the next character to be read. When reaching the end of the buffer the pointer cycle back to its start.
This exercises can be developed and tested on the Python version of your PC.
Exercise 4:
In this exercise I set again all columns pins to input but all row pins are set to output with value 0. I attach a GPIO interrupt on the falling edge to each of the column pins.
If any key is pressed, the interrupt is generated. In the interrupt service routine (key_change) I scan the keypad and insert the key value into the circular buffer. The circular buffer methods are used to check if data is available or to read out the key presses that have occurred.
--
Uli Raich - 2021-01-22
Comments