1006 typedef union {
1007     struct {
1008         unsigned LATA0                  :1;
1009         unsigned LATA1                  :1;
1010         unsigned LATA2                  :1;
1011         unsigned LATA3                  :1;
1012         unsigned LATA4                  :1;
1013         unsigned LATA5                  :1;
1014         unsigned LATA6                  :1;
1015         unsigned LATA7                  :1;
1016     };
1017   ......
1048 } LATAbits_t;
1049 extern volatile LATAbits_t LATAbits @ 0xF89;
 56 #define Row0  LATAbits.LATA1
 57 #define Row1  LATAbits.LATA2
 58 #define Row2  LATAbits.LATA3
 59 #define Row3  LATAbits.LATA4
 60 #define Row4  LATAbits.LATA5
 61 #define Row5  LATCbits.LATC0
 62 #define Row6  LATCbits.LATC1
 63 #define Row7  LATBbits.LATB1
 64 #define Row8  LATBbits.LATB2
 65 #define Row9  LATBbits.LATB3
Most of the I/Os from all the ports are used to drive rows and columns, the ones not busy for other functions. To have a more mnemonic naming I've further defined the ports. Beside the definitions for the rows.
 32 void SetRow(unsigned char Row)
 33 {// from 0 to 9, enable one row at a time to scan matrix
 34     switch (Row)
 35     {
 36         case 0:
 37             Row0 = ON;
 38             break;
 39             
 40         case 1:
 41             Row1 = ON;
 42             break;
 43             
 44         case 2:
 45             Row2 = ON;
 46             break;
 47  
 48         case 3:
 49             Row3 = ON;
 50             break;
 51  
 52         case 4:
 53             Row4 = ON;
 54             break;
 55  
 56         case 5:
 57             Row5 = ON;
 58             break;
 59  
 60         case 6:
 61             Row6 = ON;
 62             break;
 63  
 64         case 7:
 65             Row7 = ON;
 66             break;
 67  
 68         case 8:
 69             Row8 = ON;
 70             break;
 71  
 72         case 9:
 73             Row9 = ON;
 74             break;
 75         
 76         default:
 77             SetRowOff();
 78             break;
 79     }
 80 }
The rows are driven in sequential way, one by one every 1ms and it's easy to use those with this kind of definitions at runtime.
170 void WordSetting()
171 {// set the matrix with the words according to the new time
172     int i;
173 
174     for(i=0; i<=MAXROW; i++)
175     {// reset the matrix
176         Matrix[i]=0;
177     }
178 
179     if(Min <= 30)
180     {// after half hour the word is "to" the next hour, before is "after"
181         Matrix[6]=0b000000011110;                   // E
182     }
183     else
184     {
185         Hour++;
186         Matrix[7]=0b100000000000;                   // MENO
187     }
188 
189     switch(Min / 5)
190     {
191         case 1:
192         case 11:
193             Matrix[8] = Matrix[8] | 0b000001111110;// CINQUE
194             break;
195 
196         case 2:
197         case 10:
198             Matrix[9] = Matrix[9] | 0b111100000000;// DIECI
199             break;
200 
201         case 3:
202         case 9:
203             Matrix[7] = Matrix[7] | 0b001101111110;// UN QUARTO
204             break;
205 
206         case 4:
207         case 8:
208             Matrix[8] = Matrix[8] | 0b111110000000;// VENTI
209             break;
210 
211         case 5:
212         case 7:
213             Matrix[8] = Matrix[8] | 0b111111111110;// VENTICINQUE
214             break;
215 
216         case 6:
217             Matrix[9] = Matrix[9] | 0b000000111110;// MEZZA
218             break;
219 
220         default:
221             break;
222     }
223 
224     if(!TimeSync)// light up points only if RTC time is in sync with INRIM
225     {
226         switch(Min % 5)
227         {
228             case 1:
229                 Matrix[0] = Matrix[0] | 0b000000000001;// first point
230                 break;
231 
232             case 2:
233                 Matrix[0] = Matrix[0] | 0b000000000001;// first point
234                 Matrix[1] = Matrix[1] | 0b000000000001;// add second point
235                 break;
236 
237             case 3:
238                 Matrix[0] = Matrix[0] | 0b000000000001;// first point
239                 Matrix[1] = Matrix[1] | 0b000000000001;// add second point
240                 Matrix[2] = Matrix[2] | 0b000000000001;// add third point
241                 break;
242 
243             case 4:
244                 Matrix[0] = Matrix[0] | 0b000000000001;// first point
245                 Matrix[1] = Matrix[1] | 0b000000000001;// add second point
246                 Matrix[2] = Matrix[2] | 0b000000000001;// add third point
247                 Matrix[3] = Matrix[3] | 0b000000000001;// add fourth point
248                 break;
249 
250             default:
251                 break;
252         }
253     }
254     
255     Hour = Hour % 12;
256 
257     if(Hour == 1)
258     {
259         Matrix[1] = Matrix[1] | 0b101000000000;// E' L'
260     }
261     else
262     {
263         Matrix[0] = Matrix[0] | 0b111101101110;// SONO LE ORE
264     }
265 
266     switch(Hour)
267     {
268         case 1:
269             Matrix[1] = Matrix[1] | 0b000111000000;// UNA
270             break;
271 
272         case 2:
273             Matrix[1] = Matrix[1] | 0b000000011000;// DUE
274             break;
275 
276         case 3:
277             Matrix[2] = Matrix[2] | 0b111000000000;// TRE
278             break;
279 
280         case 4:
281             Matrix[5] = Matrix[5] | 0b111111100000;// QUATTRO
282             break;
283 
284         case 5:
285             Matrix[6] = Matrix[6] | 0b111111000000;// CINQUE
286             break;
287 
288         case 6:
289             Matrix[5] = Matrix[5] | 0b000000001110;// SEI
290             break;
291 
292         case 7:
293             Matrix[4] = Matrix[4] | 0b000000111110;// SETTE
294             break;
295 
296         case 8:
297             Matrix[2] = Matrix[2] | 0b000111100000;// OTTO
298             break;
299 
300         case 9:
301             Matrix[2] = Matrix[2] | 0b000000011110;// NOVE
302             break;
303 
304         case 10:
305             Matrix[3] = Matrix[3] | 0b111110000000;// DIECI
306             break;
307 
308         case 11:
309             Matrix[3] = Matrix[3] | 0b000001111110;// UNDICI
310             break;
311 
312         case 0:
313             Matrix[4] = Matrix[4] | 0b111111000000;// DODICI
314             break;
315 
316         default:
317             break;
318     }
319 
320     SetColB();  
321 }
For the columns is a little bit more complicated. All 12 of them must be set at in the 1ms cycle. In order to save precious time some tricks have been used.
The matrix has been defined with an array where the index defines the 16 bit variable corresponding to each row and the bits of each variable define the single column's LED, according to this worksheet, and translated in code with the procedure WordSetting().

But the translation (to the left) is to heavy to be performed every 1ms, because it requires a lot of masking operations to set each port.

For this reason it is performed only at each change of display, i.e.: every minute and the result is buffered in a matrix of unsigned int. This wastes more memory (one int for each bit) but...

 82 void SetColB(void)
 83 {// pre-fill the matrix with bytes instead of bits.
 84  // This wastes memory but saves a lot of time during runtime matrix scan
 85     char Col;
 86     char Row;
 87     unsigned int Mask;
 88 
 89     for(Row = MINROW; Row <= MAXROW; Row++)
 90     {
 91         for(Col = MinCol; Col <= MaxCol; Col++)
 92         {
 93             Mask=BitMask[Col] & Matrix[Row];
 94 
 95             if(Mask)
 96             {
 97                 MatrixB[Row][MaxCol-Col] = ON;
 98             }
 99             else
100             {
101                 MatrixB[Row][MaxCol-Col] = OFF;
102             }
103         }
104     }
105 }
107 void SetCol(unsigned char Row)
108 {/* from 'A' to 'L', enable all the column to light all the LEDs interested
109     the byte matrix is pre-computed when LEDs to display change (every minute)
110     this saves a lot of time during 1ms column scan
111 
112      * RD0 = Column A
113      * RD1 = Column B
114      * RD2 = Column C
115      * RD3 = Column D
116      * RD4 = Column E
117      * RD5 = Column F
118      * RD6 = Column G
119      * RD7 = Column H
120   *
121      * RE0 = Column I
122      * RE1 = Column J
123      * RE2 = Column K
124   *
125      * RC5 = Column L
126   */
127 
128     ColA = MatrixB[Row][11];
129     ColB = MatrixB[Row][10];
130     ColC = MatrixB[Row][9];
131     ColD = MatrixB[Row][8];
132     ColE = MatrixB[Row][7];
133     ColF = MatrixB[Row][6];
134     ColG = MatrixB[Row][5];
135     ColH = MatrixB[Row][4];
136 
137     LATD = Dbits.Port;
138 
139     ColI = MatrixB[Row][3];
140     ColJ = MatrixB[Row][2];
141     ColK = MatrixB[Row][1];
142 
143     LATE = Ebits.Port;
144 
145     ColL = MatrixB[Row][0];
146 
147     LATC = PORTC | Cbits.Port;
148 }
... the conversion is performed in 36us instead of hundreds of us. In this way it wastes only a little percent of 1ms period, allowing the dimming of the LEDs through the reduction of the duty cycle.
Mnemonic definitions
Rows Setting
Words setting on the matrix
Pre-setting for the columns
Dynamic setting for the columns
In order to save time during the frequently executed procedures other values, such as BitMask[] and DutyTab[] are pre-computed at startup or in less critical parts of the program.
182 // Low priority interrupt vector
183 void interrupt low_priority low_isr (void)
184 {  
185     .......
244 
245     if (PIR1bits.ADIF)
246     {// A new AD value has been read
247          PIR1bits.ADIF=0;                // reset interrupt flag
248          DutyCycle=(ADRESH*LightIndx)>>8;//only the highest 8 bits are read
249                                          //>>8 to convert back index to int
250     }
251 
252 }
The ambient light is read by the LDR through ADC peripheral and...
160 void SetTimer1(unsigned char DutyCycle)
161 {
162 //set both High and Low registers for Timer1
163 int Count = DutyTab[DutyCycle];
164 TMR1H = Count >> 8; // byte High
165 TMR1L = Count;      // byte Low
166 T1CONbits.TMR1ON=1;
167 }

...converted in time with the aid of DutyTab[] values.

In this way the TMR1 switches off the columns dimming the LEDs brightness according to the computed duty cycle.

Low priority ISR
Define PWM duty cycle
323 void TestMatrix()
324 {
325     static unsigned char Xcol;
326     static unsigned char Yrow;
327     int i;
328     static int j = 0;
329     static int k = 0;
330 
331    
332     for(i=MINROW; i<=MAXROW; i++)
333     {// reset the matrix
334         Matrix[i]=0;
335     }
336     
337     switch(j)
338     {
339         case 0: // char display
340             WriteMatrixChar('G', 'O', 7, 1);
341             TestTime = 5000;
342             j++;
343             break;
344 
345         case 1: // dot by dot
346             Matrix[Yrow]=0b0000100000000000 >> Xcol;
347             if((++Xcol)>MaxCol)
348             {
349                 Xcol = MinCol;
350                 if((++Yrow)>MAXROW)
351                 {
352                     Yrow=MINROW;
353                     j++;
354                 }
355             }
356             TestTime = 100;
357             break;
358 
359         case 2: // row by row
360             Matrix[Yrow]=0b0000111111111111;
361             if((++Yrow)>MAXROW)
362             {
363                 Yrow=MINROW;
364                 j++;
365             }
366             TestTime = 300;
367             break;
368 
369         case 3: //column by column
370             Matrix[0]=0b0000100000000000 >> Xcol;
371             Matrix[1]=0b0000100000000000 >> Xcol;
372             Matrix[2]=0b0000100000000000 >> Xcol;
373             Matrix[3]=0b0000100000000000 >> Xcol;
374             Matrix[4]=0b0000100000000000 >> Xcol;
375             Matrix[5]=0b0000100000000000 >> Xcol;
376             Matrix[6]=0b0000100000000000 >> Xcol;
377             Matrix[7]=0b0000100000000000 >> Xcol;
378             Matrix[8]=0b0000100000000000 >> Xcol;
379             Matrix[9]=0b0000100000000000 >> Xcol;
380             if((++Xcol)>MaxCol)
381             {
382                 Xcol = MinCol;
383                 j++;
384             }
385             TestTime = 300;
386             break;
387 
388         case 4: //diagonal left
389             Matrix[0]=0b0000100000000000 >> Xcol;
390             Matrix[1]=0b0000010000000000 >> Xcol;
391             Matrix[2]=0b0000001000000000 >> Xcol;
392             Matrix[3]=0b0000000100000000 >> Xcol;
393             Matrix[4]=0b0000000010000000 >> Xcol;
394             Matrix[5]=0b0000000001000000 >> Xcol;
395             Matrix[6]=0b0000000000100000 >> Xcol;
396             Matrix[7]=0b0000000000010000 >> Xcol;
397             Matrix[8]=0b1000000000001000 >> Xcol;
398             Matrix[9]=0b0100000000000100 >> Xcol;
399             if((++Xcol)>MaxCol)
400             {
401                 Xcol = MinCol;
402                 j++;
403             }
404             TestTime = 300;
405             break;
406 
407         case 5: //diagonal right
408             Matrix[0]=0b0000000000000100 << Xcol;
409             Matrix[1]=0b0000000000001000 << Xcol;
410             Matrix[2]=0b0000000000010000 << Xcol;
411             Matrix[3]=0b0000000000100000 << Xcol;
412             Matrix[4]=0b0000000001000000 << Xcol;
413             Matrix[5]=0b0000000010000000 << Xcol;
414             Matrix[6]=0b0000000100000000 << Xcol;
415             Matrix[7]=0b0000001000000000 << Xcol;
416             Matrix[8]=0b0000010000000001 << Xcol;
417             Matrix[9]=0b0000100000000010 << Xcol;
418             if((++Xcol)>MaxCol)
419             {
420                 Xcol = MinCol;
421                 j++;
422             }
423             TestTime = 300;
424             break;
425 
426        case 6: //all LEDs on
427            if(k%2)
428            {
429                 Matrix[0]=0XFFFF;
430                 Matrix[1]=0XFFFF;
431                 Matrix[2]=0XFFFF;
432                 Matrix[3]=0XFFFF;
433                 Matrix[4]=0XFFFF;
434                 Matrix[5]=0XFFFF;
435                 Matrix[6]=0XFFFF;
436                 Matrix[7]=0XFFFF;
437                 Matrix[8]=0XFFFF;
438                 Matrix[9]=0XFFFF;
439            }
440            else
441            {
442                 Matrix[0]=0;
443                 Matrix[1]=0;
444                 Matrix[2]=0;
445                 Matrix[3]=0;
446                 Matrix[4]=0;
447                 Matrix[5]=0;
448                 Matrix[6]=0;
449                 Matrix[7]=0;
450                 Matrix[8]=0;
451                 Matrix[9]=0;
452            }
453 
454             if((++k)>8)
455             {
456                 k = 0;
457                 j++;
458             }
459             TestTime = 1000;
460             break;
461 
462         default:
463             j=0;
464             TestTime = 300;
465             break;
466     }
467 
468     // Matrix[0]=0b0000111111111111;
469 
470     SetColB();
471 }

This procedure is compiled just when needed in order to test all the matrix hardware and functionality.

 

Matrix test

It also includes the display of the characters using a 7x5 font.
With the methods used to drive the matrix has been really easy to use one of the many font descriptions available on the Net. The only problem was to swap rows and columns because normally a columns scanning is used instead of rows scanning as I do. This task has been done using this worksheet.

To the right a video that shows how this test works.

update on 13-08-2014
Software

Software

Matrix scan procedures and techniques
To drive the matrix we need 10 I/O ports of the PIC18F4620 for the rows and 12 for the columns. It results in a 22 pins busy out of the 40 available in this version of the MCU.

The rows, from 0 to 9, are selected on by one with 1ms period, resulting in a 10ms total period. The 100Hz frame rate is good to have a flicker free display.
The columns, from A to L, are selected in parallel when the corresponding row is lighted up.

Beside an example of the PIC header file that shows how the ports are mnemonically defined. The example is for PORTA.

The whole MPLABX C project
for the PIC18F4620
is available as an open source at
GitHub repository