logo elektroda
logo elektroda
X
logo elektroda

PIC12F683 and SDCC - whole clock and thermometer on two pins (no external libraries)

p.kaczmarek2 
Board with PIC microcontroller and LED display. .
Today I'm going to show the project on a tiny eight-pin PIC microcontroller in a DIP8 case offering a modest 3.5kB of Flash memory. What's more, I will use only two pins to communicate with the peripherals - the whole thing will be based on I2C. Based on these I will run a display/keyboard controller and a thermometer. Finally, I will make a first attempt to enrich the whole thing with a countdown timer so that the circuit built can act as a clock. There will also be an option to set the current time.

This mini-project is somewhat related to my SDCC tutorial for the PIC18F2550 and I will here partly build on the steps described there:
Part 1 - Setting up the operating environment
https://www.elektroda.pl/rtvforum/topic3635522.html#18304424
Part 2 - Blink LED, IO pins, digital inputs and outputs
https://www.elektroda.pl/rtvforum/topic3647884.html#18389188
Part 3 - Oscillator settings. Internal oscillator, external oscillator, quartz resonator, PLL
https://www.elektroda.pl/rtvforum/topic3657704.html
Part 4 - Timers, interrupts
https://www.elektroda.pl/rtvforum/topic3676645.html#18580858
Part 5 - Seven-segment display operation
https://www.elektroda.pl/rtvforum/topic3676650.html#18580877
Part 6 - MM5450 LED display driver
https://www.elektroda.pl/rtvforum/topic3845301.html
The table of contents will be completed as I write more parts.

Related topic about the PIC12F683:
PIC12F683 and SDCC - tutorial - creating a simple dimmer (read datasheet notes) .

I will write the whole thing in SDCC under PIC12F683 without external libraries .

For practical reasons, I will not repeat the knowledge from the previous parts in detail here.


Project plan .
I will start by running the PIC12F683 in internal oscillator mode. Here I will mainly focus on configuring its pins from the oscillator as GPIOs, as I already showed the rest in the previous section. Then I will use these two pins to communicate with the display/keyboard controller. I will first start showing the individual digits and then reading the keys. At this stage I'll have more or less the software functions for I2C ready, so I'll try to use them to additionally connect a simple TC74 thermometer to the same bus and read the results from that too. Finally, I will deal with the more "clock" stuff, i.e. I will implement simple timekeeping based on a hardware timer from the PIC, and finally I will put it all together so that both time and temperature can be displayed, and the current time can be set using buttons.

The project will be continued in the second part.

Parts used .
In summary, to project I will use:
- PIC12F683 in DIP08 enclosure - main microcontroller
Three PIC12F683 microcontrollers on a wooden surface. .
Specification table for PIC12F683 device
- display from tuner with controller type TM1650/TM1637/HD2015/itd, also similar modules can be purchased online, their communication protocols are often similar but not identical
Close-up of a circuit board with capacitors and a connector.
- TC74 (thermometer with simple I2C-based interface)
Close-up of a TC74A5 temperature sensor
- PICKIT2 for PIC programming (or any other programmer that can handle it)
PICKIT 2 programmer interface with active LED indicators. .
- optionally, a 5V power supply to power the finished project, in my case from an old tuner
Electronic board with capacitors and other components optional.
- optionally, a logic state analyser to see what is happening on the lines from I2C....
Logic analyzer connected to a computer on a wooden surface. .

Step 1: Blink .
To start with, I always run the 'blink' programme which is the simplest LED flashing. This allows me to make sure that the firmware actually starts. I try to avoid situations where, for example, a program is uploaded but not executed, e.g. due to the MCLR (RESET) pin not being connected according to the datasheet. I also make sure then that I upload a firmware compatible with the MCU I have on the table.
So I started according to the previous topic in the series:
PIC12F683 and SDCC - tutorial - we create a simple dimmer (read the datasheet notes) .
Here, however, I have made a minor correction as I noticed that the 'clock' pins do not flash along with the others:
Pin connection diagram for the PIC12F683 microcontroller in a DIP8 package .
You have to search for what is wrong. Type "CLKIN" into the search engine in the catalogue note and browse the results:
Oscillator configuration table for a PIC microcontroller. .
The Config register (configuration word) defines the role of these pins. We want to set both of them in GPIO mode, so we add the CONFIG register setting to the internal oscillator without CLKOUT:
Code: C / C++
Log in, to see the code
.
All code:
Code: C / C++
Log in, to see the code
.
Works:


.

Step 2: Getting the display .
The display used (or rather the displays, as I first carried out the project on one board and after a break started on a second, similar one) are from an old tuner:
Interior of a DVB decoder with visible mainboard, power supply, and capacitors. Interior of an old tuner with visible ports and electronics. .
Inside there is usually the main CPU, Flash memory, RAM, separately the head, and, well, the keyboard/display chip that interests me:
Close-up of a printed circuit board with integrated circuits. Close-up of a circuit board with an EtronTech processor. Close-up of an electronic circuit board with various components. Circuit board with various electronic components on a wooden background. .
One advantage of using such equipment is a free power supply with an output voltage of generally 5V, although 3.3V on the board (after the step-down converter) will also be found:
Person measuring voltage on a motherboard with a multimeter. Multimeter and electronic circuit board with voltage measurement .
In this case the power supply is based on the PN8136:
Close-up of PN8136 integrated circuit and other electronic components .
A second plus is the display often found in such products with control based on an I2C-like protocol (but generally without chip addresses?):
Pinout diagram of FD650B chip in DIP16 package. LED display connection schematic with control unit .
Communication with the chip here is over two lines only - SDA and SCL, data and clock. Communication is bi-directional, the SDA is capable of both sending data and reading it. So, it's time to connect the SDA and SCL to the two selected legs of the PIC and start the adventure....
However, if you want to buy such a display, interesting modules can be found on the web:
Set of digital display and key modules. .

Step 3: I2C communication .
FD650 has already been run by someone on the forum:
4x7seg LED controller - FD650B + AVR (Bascom) .
The FD650 is not compliant with the I2C standard, as it does not have its own address separately, but instead directly "exposes" registers to the bus. These exposed registers are several, so an I2C scan will see it as several devices. In addition, this means that you won't connect two FD650Bs on one I2C bus, as you can with e.g. the MCP23017 if you set them to different addresses....
By the way, it resembles very strongly the popular TM1650/HD2015/CH455H which I have already discussed. I refer you to the following topics for details :
Running the HD2015 display/button controller after reverse engineering, comparison with TM1637itd .
Read the datasheet note and write the CH455H 7-segment LED display driver in Arduino .
7-segment displays on TM1637 - 4 and 6 digits - Arduino, protocol .
The register addresses match. Based on the above topics, we have:
- 0x48 - configuration register
- 0x68, 0x6A, 0x6C, 0x6E - character registers
So I have rewritten to SDCC my code from the above mentioned topics:
Code: C / C++
Log in, to see the code
.
It works, the segments of the first digit flash.
Electronic board with connected wires and LED display .
In order to validate the code I suspected the whole thing on a logic analyser from the subject Salae 24MHz logic state analyser for £40 - analysis of unknown LED display protocol :
Screenshot from PulseView logic analyzer showing I2C signal waveforms. .


Step 4: Countdown and characters .
In this case, the character map I developed in a previous topic also fitted. It is also compatible with commercially available TM1650/TM1638 modules, etc:
Code: C / C++
Log in, to see the code
.
You get to the segments of a given digit (or even more broadly, character, as I have up to F characters) by simply indexing the array with the given value.
To start with, I fired up the simplest "countdown":
Code: C / C++
Log in, to see the code
.
The result:


.
Based on this, you can make a function that splits a number into digits using integer and modulo division operations; below is a three-digit version:
Code: C / C++
Log in, to see the code
.
And here the "watch" version, i.e. two separate numbers from 0 to 99:
Code: C / C++
Log in, to see the code
.
With the above function I can already display the time in the form HH:MM or MM:SS.

Step 5: I2C scan .
I then decided to try connecting the second devices on my I2C bus. I wasn't sure how the display controller would behave here, which essentially doesn't have its own single call address, but responds immediately to register addresses. Will it respond to the ack every time?
I had the I2C scanner from this topic:
Building a clock on a PIC18F2550, step by step - part 1, BMP280, TC74, 74HCT164, I2C .
I ported it to myself and modified it to display the corresponding addresses:
Code: C / C++
Log in, to see the code
.


.
Now it was time for two separate scans:
- the first without TC74 on the bus
- the second with the TC74, so as to know its address
I simply performed the scans and compared the results.
I got addresses 72, 74, 76, 78, 104 and 106 without the TC74, and the TC74 itself contributed an additional address 154. So it is seen, it is time to read it.

Organisational note .
Due to external factors, I had to delay the project a bit and in the end I carried out the second stage of the project on a slightly different board from the tuner. The digit registers have not changed, but the register of e.g. the buttons has. The rest of the code is unchanged. I have already presented the used board in one of the topics:
Read the datasheet note and write the CH455H 7-segment LED display driver in Arduino .


Step 6: Reading from the TC74 .
I got the TC74 reading code from the topic about the clock:
Building a clock on a PIC18F2550, step by step - part 1, BMP280, TC74, 74HCT164, I2C .
The TC74 is very simple to use, the temperature is as an 8-bit, one byte number, read from a register at address 0x00:
Register and temperature conversion table for TC74 .
We first send the register address, here 0x00, preceded by the thermometer address with the W bit (youngest bit turned off) to the thermometer, and then send the byte read, also preceded by the thermometer address, but with the R bit (youngest bit turned on):
Code: C / C++
Log in, to see the code
.
For simplicity, I did not do a proper I2C "restart" in the code for now, just a separate "stop" and "start".
Result:
Electronic board with a display showing 01:78. .
It works, it's about 17°C in my studio.

Verified with Salae logic state analyser :
I2C signal diagram showing data read process. .

Step 7: Buttons .
As analysed earlier:
We read the datasheet note and write the CH455H 7-segment LED display driver in Arduino .
The state of the buttons is in a register with address 0x4F, except that it's not simply a bitmap. Anyway, let's check:
Code: C / C++
Log in, to see the code
.
You can see from the video that there is seemingly something wrong::


.
The eight-bit word read out turns out to be encoded information about a single button, more precisely its code and separately its pressed state - pressed or released.
Code: C / C++
Log in, to see the code
.
I uploaded the program and examined the responses to the buttons. There are three on my board, their codes are sequentially 7, 15, 23. Based on this, I have added a button handler which examines what is pressed and, for example, changes one variable:
Code: C / C++
Log in, to see the code
.
Analogous editing of the current time will be done soon.

As usual, preview the transaction in Pulse View:

I2C data transmission diagram between microcontroller and peripheral device. .

Step 8: Countdown time .
Countdown time is a little more difficult than it might seem. It would be best to use an external RTCC (Real-Time Clock/Calendar) chip for this, such as the MCP7940N:
Block diagram of I²C RTCC with oscillator components and connections. .
The advantage of such a solution is that the time is kept even after a loss of power. A small battery or cell is often used for this. Newer PICs also have RTCC built in.
Here, however, in order to simplify things, I will try timekeeping with a timer:
Tutorial PIC18F2550 + SDCC - Part 4 - Timers, interrupts .
Let's take Timer 0 as an example:
Document snippet detailing Timer0 module functionality in a microcontroller. .
Block diagram of the TIMER0/WDT prescaler .
I set the internal oscillator earlier to 4MHz, so Fosc/4 is 1MHz.
The prescaler can now be selected in OPTION_REG:
Timer0 and WDT prescaler configuration table .
I chose 1:256, so the timer incrementation will occur not at 1MHz, but at 3.90625 kHz, so the period is 0.256ms.
Now you still need to set from which value the timer will increment:
Excerpt from documentation describing the INTCON register and its functions. .
The interrupt is called when the value of TMR0 overflows (exceeds 255, because it's an 8-bit register), so setting TMR0 to 0 will cause the interrupt to call every 256 timer cycles. Since we know that the timer period is 0.256ms, the period of 256 cycles will be 65.53600 ms....
Well, here's a little problem - it's a bit hard to add such counts, it would be much easier to be able to set some better summing period, but for that you would have to change the clock to e.g. the classic 32.768kHz .
32768Hz is not a random value, it is a value of 2 to the power of 15. As if we had Fosc = 32.768kHz, then Fosc/4 would be 8.192kHz. Then by adding a 1:256 prescaler we would get a timer increment every 32Hz, so setting TMR0 to 32 would give us an interrupt every second....
However, I opted for this less precise, inferior solution to begin with and ended up with something like this - timer initialisation and interrupts:
Code: C / C++
Log in, to see the code
.
Timer interrupt:
Code: C / C++
Log in, to see the code
.
Here you can see, among other things, why a clock resonator would be better - some time is simply lost here. It would be possible to count in decimal parts of ms, but still far from ideal....
Code: C / C++
Log in, to see the code
.
In this situation, we count 65.5ms and lose 0.036ms every second on the calculations alone. A day has 86400 seconds, so we lose 3.1104 seconds per day.... 90 seconds in a month.
One could try to make the correction programmatically....
Code: C / C++
Log in, to see the code
.
However, tests showed that this is not ideal, probably because I did not take into account the inaccuracy of the internal resonator and the instruction execution time.... I experimentally reduced the value of 536 to 200, each time testing the clock separately for a few hours with a stopwatch. Now on a scale of a few hours I am not able to observe latency/speed, for a start it will be enough as I am planning an external RTCC anyway..

You could also transfer this calibration to the EEPROM and just play around for a few days to set it up properly. The contents of the EEPROM are remembered when power is lost, and the PIC used has 256 bytes of it.

Step 8: Time setting .
I implemented the time setting based on the button code shown earlier. I entered a variable to specify the state of the clock (normal operation, hour change, minute change):
Code: C / C++
Log in, to see the code
.
and then I separately connected the button events:
Code: C / C++
Log in, to see the code
.
Ultimately you can also loop values here, the hour will be in the range [0,23] and the minutes in the range [0,59].
In addition, in the main loop I do a flashing of the currently edited value (hours or minutes):
Code: C / C++
Log in, to see the code
.
This lets us know if and what editing mode is enabled.


Step 9: Double-entry .
I have experimentally determined that the colon is plugged into register 0x6C and has a bit code of 32. I have accordingly changed the display code to always light this bit:
Code: C / C++
Log in, to see the code
.


Step 10: Alternate display of time and temperature .
Finally, I have introduced a simple mechanism for displaying alternating time and temperature. It is based on a "frame"/"refresh" counter, in each loop I check the index of the "frame" and display the selected value based on it. In addition, I check if the edit mode is not active, if so, I always display the time.
Code: C / C++
Log in, to see the code
.

Effect at the moment .
The clock alternately displays the current time and temperature. The time can be set, although the buttons do not distinguish between when the key is pressed and when the key is held down continuously:


.

Summary .
Probably the most interesting conclusion from all the fun is that even with these Chinese inventions like TM1650, TM1637 etc., where normal I2C-compatible addressing is missing, it is still possible to give several devices on one bus. I don't mean two TM1650s, of course, as this would be impossible due to the same register addresses, but e.g. TM1650 + TC74 can be made to work.
The clock in its current form works, it works properly on the power supply from the board used, and 5V additionally can also be taken from it.
Of course a lot could still be developed and improved, so in the next part I plan to:
- an attempt to connect and program some RTCC so that the time is counted outside the PIC and additionally is supported by a 3V battery when I disconnect the clock from the mains (or reset the PIC, each upload of a new firmware resets it)
- an attempt to think about and introduce some form of alarm, maybe with a buzzer, also with saving the settings in EEPROM memory, the PIC used has 256 bytes of it
- an attempt to connect some kind of port expander on I2C, if the addresses allow...
- reorganising the code, especially the control and refreshment, to make it more sensible
That's it for now. In the next part we will see how much more Flash memory there is and to what extent I will be able to develop this project further. I am counting on at least these RTCCs... .

I am attaching the "project" (code and batch script for compilation) for reference:

About Author
p.kaczmarek2
p.kaczmarek2 wrote 12047 posts with rating 10067 , helped 577 times. Been with us since 2014 year.

Comments

Sam Sung 10 Nov 2024 18:17

All nice, but how to get rid of the unnecessary load, i.e. SoC and RAM, from the board of such an STB? This is probably the most difficult step, because you have to precisely solder out the dense chips... [Read more]

p.kaczmarek2 11 Nov 2024 00:07

As I understand it, you are asking about using hot air for the discharge so that there are no short circuits? I remove short circuits with a braid, in addition I use flux. The braid allows you to effectively... [Read more]

XJ_ 20 Nov 2024 17:21

Wau, pretty old skool wrx. Kłaniam się, proszę pana !!! Added after 3 [minutes]: Like I do... If blinking, everything gona be alright ;-) [Read more]

%}