The Main Board

I created the main board in the same pattern as the original. That made for quite an expensive prototype as there is a lot of empty space. This size is needed to fit the tactile switches in their proper places. Now that I have Revision 2 up and running, a write up of what I have learned along the way is over due.

The assembly was not without issues. There were some lessons to learn with KiCad and circuit design and layout as I did make some mistakes but I was able to identify the issue and make some corrections. Here are some of the things I picked up along the way

Check the pin outs

This might sound like an obvious thing and it hind-sight, it was. Complacency crept in on more than one occasion and I did not verify the pin allocations between the datasheet, symbol and footprint for the linear regulator. As it was a three pinned device, I only needed to reset it and run a bridge to ground.

LEDs

I did not factor any indicator LEDs on the main board. While this is not a mistake, the addition of an LED to blink to show that something is happening is well worth the few seconds it takes to model it in the schematic and layout.

Don’t leave GPIO pins unconnected

This is just to say for any prototyping board, it would be worthwhile to expose any spare pins to headers, and or coupled with 0Ω resistors. When troubleshooting, you never know when you might need something. Of course in the production board, these would not be populated.

Getting Things up and Running

Heartbeat-Bodge-bOnce the power rails were reading the correct voltage, I connected in the RTC module. This fired up OK. I.e. Its LED lit up for a second. This is when I realised I was missing a heartbeat LED. I simply connected a piece of through hole lead to Port B0. This was the output that was configured as the heartbeat on the myAVR development board.

 

Using the procedure I used for the previous boards, I tested the connection between the PC and the AVRISP mkII using the Atmel Studio. If I could read the serial number of the micro, then I knew I was in good form.

SystemTest-01b
Main Board and Rev1 of the Display Module

I flashed the program I was using on the myAVR Dev board. Straight away the heartbeat started pulsing. Another good sign. The display did not react, or when it did, it was with garbage.

The issue with the display module as due to a mismatch between the main board and the display module. Since communication was OK to the RTC, I could assume that the SPI was largely OK. The obvious place the issue would be was with the actual signalling and or timing.

This issue has kept me occupied for some time. The biggest help was the Salea Logic Analyzer. To be able to capture the signals and have the digital values interpreted is a huge advantage. I could read the values being sent to the clock and display to ensure they were correct. I was able to tweak the code so that I could match the SPI specifications described on the RTC datasheet. I used this as a base to then configure the display module. Everything started to fall into place then and the display was more stable.

I found that to get the flexibility out of BASCOM, I needed to set up the Master i.e. the main board using software controlled SPI. Whereas, I could simply rely on the hardware SPI for the slave (display board).

Main Board SPI Setup

Fir the main board, the software controlled SPI was preferred since I could then use the SS = None property on the configuration. I then had to allocate my own chip-select lines for each of the RTC and the display module.

Config Spi = Soft , Din = Pinb.4 , Dout = Portb.3 , Ss = None , Clock = Portb.5 , Setup = 40 , Mode = 1

Display_ss Alias Portb.2
Config Display_ss = Output : Display_ss = 1

Dcf_ss Alias Portb.1
Config Dcf_ss = Output : Dcf_ss = 1

Spiinit

Display Board SPI setup

The key to the display board setup was to use the Master = no to ensure that this would be configured as a slave. All pin allocations were as per the datasheet for the micro controller.

Config Pinb.4 = Output
Config Pinb.3 = Input                                       ' MISO
Config Spi = Hard , Interrupt = On , Data Order = Msb , Master = No , Polarity = Low , Phase = 0       ' , Clockrate = 128

Spiinit

On Spi Spi_isr                                              'Nosave

In principle, things were good. I know I can flash the display module and I know I can flash the main board. I also know that communication is happening between the main board and the RTC.However there is still a niggling problem between the controller board and the display unit. After a while, the data seems to get out of sync and the display starts to display wrong values. SignalToDisplay Examining the SPI signals, I could be satisfied that the data presented and received by the display module is correct. There is something else happening inside the display module that is causing the information to be gabled. This is where I am reaching another boundary on my tool-set. The myAVR and the AVRISP MkII do not support any debugger. This is a set of tools I have yet to expose myself to and learn. Thinking around the problem, I am sure that this is some type of synchronisation issue. The actual cause, I can’t be sure. One thought would be some interference with the interrupt service routines. Since the data presented to and received by the display module is correct, I am willing to exclude the actual controller from the equation, for the moment. The problem has lead to some optimisations on the display module. One of the first improvements was seen when removing the NOSAVE from the On spi Spi_isr statement. This reduced the Spi_isr down to the following

Spi_isr:
   Set Rbit
   Mosi = SPDR
Return

The same routine as described in a previous blog entry also contained logic to arbitrate whether the incoming data was a register address or a value for the register. in fact, this was always a concern. How to reliably determine the name from the value. The very first entry after start-up is going to be an address. After that, if something happens to the timing, it is not so clear cut.

After trying several ways to analyse the problem and make changes that were not always successful, I have determined the most effective way would be to flag the address. This means the API to the display module has now changed such that the first byte send to it, as address, must have the most significant bit set. This is a major improvement on reliability, but still no solution!

Do
   If Rbit = 1 Then
      Reset Rbit
      Spdr = Mosi

      If Mosi.7 = 1 Then
         Idx = Mosi And &B00001111
      Else
         Register(idx) = Mosi ' Assign the data to the register
         Select Case Idx      ' Based on the register/value, set the affected variables
            Case Hours_10:
               Digits(1) = Register(idx)
       ...

There are still some issues to solve with this. For the above change, the controller board was not considered. However, the problem now comes back to the controller board to understand what the interaction is between the RTC, the controller board and the display board really is. Only once in a while at the transition of a minute, the display will be corrupted for a second or two. This does not happen often. Perhaps three to four times in an hour. But it shows there is still some form of timing race condition happening.

Where to from here?

I am happy that the communication is happening but I can’t call the job complete as there are some odd issues still persisting. I could work around them by sending only one byte rather than two so that the high end nibble is the address and the low end nibble is the value to be set. This would work for most of my values, but no all. Besides – that would be a work around and not a solution. I need to find a way to better understand what is happening with the data between the RTC, main board and display module such that the display modules register address and values are being skewed. At least that is my current understanding of the issue.

 

 

Further testing the μController Display Module

In the last post, I was able to get the μController version of the display module working with a basic test routine that proved that 1. the controller could be programmed and 2. the display was being driven correctly. The next stage is to flesh this out to replicate the MAX2771 API. The purpose here is not to simply create a duplicate of the MAX2771 which does a great job on its own. This approach means that I can re-use the code I created for the MAX2771 display module project. The additional goal of this small project was to gain experience in creating a SPI slave! Interfacing with the MAX2771 was simple since that is already a SPI slave unit. All I needed was to just create the master.

Connecting things together

I still have things set up from the previous tests so it was just a matter of connecting things back up again. Though this time I had two units to program instead of just one. I still have the main controller board for the clock to complete so I used the myAVR Dev board as a provisional main controller. Luckly, the myAVR dev board comes with its own programmer. I had to program this via the myAVR ProgTool since the BASCOM-AVR IDE was in use with programming the display module via the AVRISP mkII. It sounds more awkward than what it turned out to be. The part that was awkward was that the AVRISP mkII had trouble programming the display module while the SPI connection was still made to the myAVR dev board (my provisional mainboard). I had to disconnect the /SS, MOSI and CLK from the display module before being able to program. This would be a point of research to work out what is involved to be able to program the display module while still connected!

μControlled Display Driver Integration Test
μControlled Display Driver Integration Test

Once the infrastructure of programmers and connections was sorted, it was just then a matter of piecing together the code to enable the slave unit to start servicing the requests of the master i.e to start displaying the time. With the Real Time Clock still connected (via an I2C bus) I had a neat source of test data.

Getting thing up and running was rewarded with a red blinking LED on the dev board. This was my signal that the communication with the Real Time Clock was functioning. The next was the display module itself.To keep things simple,  I am opting to use the basic BASCOM constructs for SPI. The logic analyzer was useful for seeing that the SPI messages were being sent. Though my particular logic analyzer only shows that there was activity. It lacks the sophistication of being able to see exactly what was being sent. Thankfully, I have been able to get things working without that level of sophistication.

LA6_Image_0020

Creating the Slave

Dim Rbit As Bit
Config Pinb.4 = Output                    ' MISO
Config Spi = Hard , Interrupt = On , Data Order = Msb , 
       Master = No , Polarity = Low , Phase = 0 , Clockrate = 128
Spiinit

On Spi Spi_isr Nosave

Enable Timer0
Enable Timer1
Enable Interrupts

The snippet above shows how the slave was defined. Basically I used the same construct in the master. The “Master = yes” property is the only difference. Once data is available for the slave to read, the spi_isr interrupt routine is executed. Here I just grab the data into a Regisiter array. The API I was wanting to implement was to read a two byte command i.e. a register address and then a value. I was not sure on how this is to be done since BASCOM-AVR documentation only really talks about receiving one byte at a time for slave. In comparison, sending multiple bytes from the master is not difficult at all.

Spi_isr:
   PUSH r24    ; save used register
   IN r24, SREG; save SREG
   PUSH r24
   Set Rbit                         ' we received something
      If Spi_idx = 0 Then
         Idx = SPDR
         Set Spi_idx
      Else
         Register(idx) = SPDR
         Reset Spi_idx
      End If
   POP r24
   !OUT SREG, r24 ; restore sreg
   POP r24       ; and the used register
Return

Here I had a variable to track which element of the command was being received spi_idx. When reset, then the first value from the master is expected and therefore that value is recorded as an index value i.e the register address. When spi_idx is set, then it is expected that the value from the master is the register value and that is then directly recorded in the Register array.

With the register address and value for the register obtained from the master, then it was a matter of interpreting the newly arrived information. I implemented this using a case construct.

   If Spi_idx = 0 Then
      Select Case Idx
         Case Hours_10:
            Digits(1) = Register(idx)
         Case Hours_unit:
             Digits(2) = Register(idx)
         Case Minutes_10:
            Digits(3) = Register(idx)
         Case Minutes_unit:
            Digits(4) = Register(idx)
         Case Colon:
            Digits(5) = Register(idx)
         Case Indicators:
            Digits(6) = Register(idx)
         ' Case Intensity:
         ' Case Power_mode:
         Case Digit_blink:
             Blink_mask = Register(idx)
         ' Case Blink_rate:
         Case Test_mode:
            If Register(idx) = Enabled Then
               Digits(1) = 8
               Digits(2) = 8
               Digits(3) = 8
               Digits(4) = 8
               Digits(5) = 8
               Digits(6) = 8
            Else
               Digits(1) = 0
               Digits(2) = 0
               Digits(3) = 0
               Digits(4) = 0
               Digits(5) = 0
               Digits(6) = 0
            End If
         Case Else
      End Select
      End If
   End If

The idea here is to simply  react if there is a new value arrived. Based on the value of idx (the register address) to then assign the value from the register to a variable that will result in the desired behaviour. i.e. setting a digit value etc.

One feature I have implemented that is not part of the MAX2771 API is a Blink feature. i.e. to enable a digit or colon to blink. I managed this with a mask with one bit for each element of the display. This seems to work quite well.

Renderdisplay_isr: 
   Timer0 = Timer0_count
   Incr Position
   If Position > 5 Then
      Position = 0
   End If

   Digit = Position + 1
   PORTD = &H0A          ' Set all segments off
   PORTC = Makebcd(position)

   Blink_test = &B00000001
   Shift Blink_test , Left , Position
   Blink_test = Blink_test And Blink_mask
   If blink_flag = 1 and blink_test > 0 Then
      Value = Digit_off
   Else
      Value = Digits(digit)
   End If
   PORTD = Value
Return

Next Steps

The μController version of the display module is working, I have to admit, in principle. There is still a bit more work to do to clean up its behaviour and possibly implement more tests and features i.e. the indicators on the top and bottom left of the display.

The actual main board still needs to be done. This will be presented in future posts. There are some considerations to be done with that in terms of its physical size for the existing casing. Another task that is outstanding is the interfacing of the Real Time Clock with SPI rather than I2C. I have been using I2C since that is what I used on the very first trial. My goal is to have a multiple slave SPI implementation. This too can be prototyped with my current set up before finalising on the actual main controller board.

The advantage I have with the current approach is that the firmware for the main board is progressively being done with the testing of the modules. It is just missing the implementation of the various push buttons. Even this can be implemented in the myAVR Development board.

Testing the μController Based Display – The Environment

This post is about bringing up the μController based display module for the Clock. In a previous post, I outlined the approach that I would be taking. Since this will be my first attempt at in-board programming on a “real” project, the idea is to take a systematic approach to try to quickly resolve any issues that might arise.

Setting up the Environment

muAVR mkII
muAVR mkII

To date, I have been working in the safe, insular world of myAVR and BASCOM on a newly configured PC. I have been lucky in that everything has “simply worked”. In order to start programming directly onto a project’s board, I need to start using the AVRISP mkII. Like I outlined in the previous post, the idea is to bread-board a controller with a LED. Using a simple “Blinker” program, I can ensure that the communication from PC to ISP to μController is correct without having to involve any issues that might be present on the display module board.

IMG_0767
AVRISP mkII
IMG_1113
Rough and ready test harness

As it turned out, this exercise was worth while. In order to communicate with the AVRISP mkII, I needed the appropriate drivers. This are installed with when installing the AVR Studio. This is a C/C++ IDE. My objective, at this stage is to stick with BASCOM. I will move to C at some later stage. In order to use the BASCOM-AVR IDE, I had to install the USB_LIB and set up a device filter, as per the BASCOM help document.

With the drivers installed correctly, I was able to see the AVRISP mkII display the correct status on its LEDs and I could then start programming the minimal “bread-boarded” μController.


$Regfile="m8def.dat"
$Crystal=4000000
$hwstack=40
$swstack=40
$framesize=60

' $baud = 4800
Config Portb = Output

Portb.0 = 1

Do
   Set Portb.0
   Waitms 1000
   Reset Portb.0
Loop
End

Next Step

The next steps will be to bring the board up slowly. That is to apply power and check that power is where it is intended to be. Once that checks out, the same test program used to verify the environment will be used to ensure that the board can actually be programmed. The test program will be the same “blink” example.

Time for a Change

AlarmClock-OriginalAs a starter exercise, I have decided to put a new life into an old digital alarm clock that stopped working. I received this in 1980 and it worked reliably well until late 2012 when if finally stopped displaying the time. It is amazing that it lasted as long as it did. The tone was not a tone but rather an annoying screech. The remarkable thing I found about this device of this age was that it was almost capable of memory. When the power was pulled, and the plug re-inserted within 5 seconds, it would not loose time. More than 5 seconds and it would give a dying squeak. Re-inserting the plug and the time reset mode would be enabled i.e. blinking display but the time would be close to the time the plug was pulled. Most clocks of the era (and today) will simply flash 12:00.

So why this exercise?

It has already been pointed out to me that I can go down the street and get a $5.00 replacement. There are many reasons to begin with this as a kick off project

  1. It has a function
  2. It will have inputs (alarm setting buttons, sleep mode)
  3. It has more than one output – display and sound
  4. These fit in with using a micro controller
  5. All have to fit into a pre-prescribed casing adding another dimension to the board design
  6. There will be a sense of achievement getting this thing working again

The original is based on a MM5402N from National Semiconductor. Rather than replacing the passives and or the clock chip, I thought this could be an opportunity to learn about simple controller programming and IO. To be honest, I did try replacing the components. But with my level of soldering rework practice and the age of the board, I gave up and decided a whole new approach was needed.

AlarmClock-CopperLayer
The board layout was pre-automated tools. No KiCad here.
MM5402N based alarm clock
The layout is remarkably simple. Seeming keeping to the application notes of the MM5402N data sheet.

To keep things simple,  I will be starting with a the proof of concept using an existing example. This uses the myAVR MkII  development board for the Atmel range of chips. This is a dev board I have started with and would like to continue to gain a bit of proficiency. I will be using an Atmega88 and the Real-Time-Clock with DCF77-Empfang as per the existing example. I like to consider the RTC-DCF for the final project also as this delegates the management of time to the Read-Time-Clock, for instance, setting and firing of alarms. This device as additional features of receiving the actual time transmitted from Frankfurt, Germany and also provides an alarm function. This leaves the device controller to manage the UI i.e. alarm setting buttons and the display. The device is a bit of an over-kill in that it also provides a number of communication possibilities. The communication mechanism that I will be using for this will be I2C – only to get some experience in this mechanism. The first problem I had was that the MyAVR operates at the nominal 5V. The RTC-DCF operates on 3.3V. So my first lesson in I2C was a level shifter to convert the signal voltage from 5V to 3.3V. This was easily solved as there are plenty of resources on line to explain this. I came up with the following based on the information in the RTC-DCF documentation and using a set of pin headers that I can then easily interface with the MyAVR. I2C Level shifter schematic   I created a GitHub project and created the schematics and board layout using KiCad. I had the boards created through OshPark. This was the simplest part so far!

The boards arrived and assembled the parts onto the board. The next stage was to connect them all together. This is the part that made me look at the documentation a bit closer to realise that in the sample application I was modelling, they used a couple of interrupts from the clock module to help with the tiboard-frontming and display of the correct time. These signals also went via a level shifter. Thankfully Oshpark always supply a minimum of three boards and my boards will handle two signals each. So I assembled another board to cater for the interrupts. The final assembly of the test setup is shown below. I used a hand held logic analyser to observe some of the signals and there I notice an anomaly. Even after getting the sample working i.e. displaying the correct time, there are a couple of things that don’t quite add up when compared with the sample and documentation.

The breadboard setup

The first issue I found was that there is supposed to be a interrupt fired every second. I have since found out that that won’t happen until there is a firmware update needed on the real-time-clock (this will be another challenge to add to the repertoire). To work around this, I implemented my own one-second pulse with one of the in-built timers of the Atmega88. This was an area where I actually had quite a bit of trouble. In another sample program, I had created and tested a display driver. But creating a “time-counter” sample was very difficult. By having to drive the 7-Segment LEDs manually and compute the time, there seemed to be some odd clash with memory that I am no longer familiar with after so many years of using higher level languages. Rather than counting up, sequentially, the display was displaying seemingly random numbers. I really could not solve this issue. Since, in principle, the display driver was working, I turned back to the problem of the clock implementation, I found a couple of rules that need to be adhered to. These rules are not exhaustive or conclusive. Just a couple of points I noticed from this exercise.

  1. Keep the Interrupt Service Routine (ISR) as short as possible. I.e any additional or fancy computations in the 7-Segment display driver could be clearly detected in the refresh rate of the display segments.
  2. Any computation that an ISR depends on should also be contained in an ISR – or something equivalent to make the computation atomic.

Adhering to these rules, the ISR for the display driver did only that – Display the time. The one-second pulse timer was used to build the string to display. Attempting to build that string anywhere else caused strange things to be displayed. By setting the display string in the one-second pulse timer ISR effectively made that operation atomic.

Logic analyser outputAside from the display issue, what else I found odd was with the second interrupt used by the sample – a “DCF time-receipt complete interrupt”. The interrupt routine for this interrupt was being fired seemingly randomly regardless of when the DCF time-receipt actually fired. The small logic analyser output shows I2 as the input from the DCF time-receipt and I4 is connected to a LED that is toggled whenever the ISR is fired. It can be seen that I4 is toggled twice before the receipt of the interrupt on I2. This really needs a closer examination of the IO settings and interrupt usage. i.e. Read-the-manual.

What now?

The proof-of-concept is basically done. To complete the exercise, the firmware update should be done on the RTC-DCF do demonstrate the RTC sourced heart-beat rather than the timer-driven pulse. I have also learned from this that I need to include a 3.3V regulator in order to use this RTC-DCF module.

One of my goals is to also learn SysML so it will be (back) to the drawing board to start to model what it is that I want from this device and what it needs to contain. i.e. Should this be using such an all-inclusive module for the time or go directly to the DCF77? Should a simple display module be used? and spare me the resource issues when manually driving four seven segment displays plus colon and alarm indicators. These and more questions will be subjects for further posts.