Some assembler required (PIC 16F59)

Thread Starter

takao21203

Joined Apr 28, 2012
3,702
I am using 8x of these for my large LED matrix.
Each controls 16x6 LEDs with 4bit for each LED.

The only possibility to transfer data serially fast into the chip I saw was to code it in assembler.

It is not so good to use on the sender side:
There must be additional delay between each byte.

Actually I even want to try hardware serial buffer with one smaller 14pin PIC for 2 16f59.

The clocking scheme transfers a bit on rising and falling edge- saving time. And I have built in a timeout, this is reducing the speed a lot. Maybe I have to remove it somehow.

There is not much time left, about 1mSec, to clock in 376 bits.
Clock is 18 MHz.

3uSec.bit / 200nSec MCU cycle (approx.)

Here the source code:

(for transfering 8bits)

I could speed it up a little by transfering 16 bits each time.

Maybe I also need to change some parts of the display control to assembler because it is too slow in C.
I get 30 Hz for 96 LEDs / 16 brightness levels, but that is free running as fast as possible.

Some speed is lost since I expand from 4bits storage to 8bit for each LED in a line.

Rich (BB code):
unsigned char asm_status @0x10;
unsigned char asm_x @0x11;
unsigned char asm_y @0x12;

void receive_bits(unsigned char x)
{asm_x=x;

 asm("banksel _asm_y");
 asm("movf _asm_y,w");
 asm("movwf FSR");
 asm("movf _asm_x,w");
 asm("addwf FSR,f");

 asm("clrf TMR0");
 asm("clrf INDF");
 asm("reloop0:");
 asm("btfss PORTB,1");asm("goto reloop0"); // wait for clock to go H
 asm("btfss PORTB,0");asm("bsf INDF,0");// get data bit

 asm("reloop1:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfsc PORTB,1");asm("goto reloop1"); // wait for clock to go L
 asm("btfss PORTB,0");asm("bsf INDF,1");// get data bit
 asm("reloop2:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfss PORTB,1");asm("goto reloop2"); // wait for clock to go H
 asm("btfss PORTB,0");asm("bsf INDF,2");// get data bit
 asm("reloop3:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfsc PORTB,1");asm("goto reloop3"); // wait for clock to go L
 asm("btfss PORTB,0");asm("bsf INDF,3");// get data bit
 asm("reloop4:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfss PORTB,1");asm("goto reloop4"); // wait for clock to go H
 asm("btfss PORTB,0");asm("bsf INDF,4");// get data bit
 asm("reloop5:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfsc PORTB,1");asm("goto reloop5"); // wait for clock to go L
 asm("btfss PORTB,0");asm("bsf INDF,5");// get data bit
 asm("reloop6:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfss PORTB,1");asm("goto reloop6"); // wait for clock to go H
 asm("btfss PORTB,0");asm("bsf INDF,6");// get data bit
 asm("reloop7:");asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");
 asm("btfsc PORTB,1");asm("goto reloop7"); // wait for clock to go L

 asm("btfss PORTB,0");asm("bsf INDF,7");// get data bit
 asm("btfsc TMR0,7");asm("goto timeout");asm("clrf TMR0");

 asm("bcf _asm_status,0");
 asm("goto rdy");
 asm("timeout:");
 asm("bsf _asm_status,0");
 asm("rdy:");
}
 

Thread Starter

takao21203

Joined Apr 28, 2012
3,702
I don't really want to use assembler but it is the only way to speed up the LED matrix drive.

Or I could overclock the PIC- it is apparently possible to run some PICs at double frequency.

the project migrated a lot- at first, I used 74hc595 buffers (but errors creeped in because too many chips). Then I changed to 8x PIc 16f59, having lots of these PCBs around.

All the faulty LEDs were replaced some day.

The files were moved a few times to different computers, and MPLABX updated many times. Luckily I had them on my skydrive.

A few days ago, I downloaded them to my netbook.

Today I tried the new Microchip Configuration manager. For hardware serial port, i use a PIC 16f1824.

I think when I run the serial lines from 8 LED matrix panels directly to the PIC32, it will be occupied too much.

The timing is complicated- because the 16f59 has no external interrupt, the master must test for condition, and then signal it wants to transfer updates. Then the 16f59 must respond to that, indeed wait for such a response for a while or otherwise skip.

Then it waits for bits to become clocked in.
Maybe I use two bits at once or maybe it is not worth it.
And I want to try to connect 2x 16f59 to one 16f1824 chip.

The 16f1824 run from internal 8 MHz with 4x PLL.
 

Thread Starter

takao21203

Joined Apr 28, 2012
3,702
This is what I created tonight for the master controller:
Rich (BB code):
void send8bits(unsigned char index)
{
 struct {
  unsigned b1:1;
  unsigned b2:1;
  unsigned b3:1;
  unsigned b4:1;
  unsigned b5:1;
  unsigned b6:1;
  unsigned b7:1;
  unsigned b8:1;   
 }ser_data=spiReadBuffer[index];
    
// CLK floating (input) + WPU=ON 
// pulled low by slave -> interrupt on master
// master sets DAT to 1 if it wants to transfer data
// slave waits for CLK to go high
   
IO_RA0_serdat0_LAT=ser_data.b1;
IO_RA1_serclk0_LAT=1;
IO_RA0_serdat0_LAT=ser_data.b2;
IO_RA1_serclk0_LAT=0;
IO_RA0_serdat0_LAT=ser_data.b3;
IO_RA1_serclk0_LAT=1;
IO_RA0_serdat0_LAT=ser_data.b4;
IO_RA1_serclk0_LAT=0;
IO_RA0_serdat0_LAT=ser_data.b5;
IO_RA1_serclk0_LAT=1;
IO_RA0_serdat0_LAT=ser_data.b6;
IO_RA1_serclk0_LAT=0;
IO_RA0_serdat0_LAT=ser_data.b7;
IO_RA1_serclk0_LAT=1;
IO_RA0_serdat0_LAT=ser_data.b8;
IO_RA1_serclk0_LAT=0;
}
There are 48 bytes to transfer.
I never used bitfields so far...
 

Attachments

Thread Starter

takao21203

Joined Apr 28, 2012
3,702
The resulting assembler code:

Rich (BB code):
!void send8bits(unsigned char index)
0x35: MOVLB 0x0
0x36: MOVWF index
!{
! union{
!  unsigned char sd;
!  struct {
!   unsigned b1:1;
!   unsigned b2:1;
!   unsigned b3:1;
!   unsigned b4:1;
!   unsigned b5:1;
!   unsigned b6:1;
!   unsigned b7:1;
!   unsigned b8:1;
!  };
! }ser_data;
!ser_data.sd=spiReadBuffer[index];
0x37: MOVF index, W
0x38: ADDLW 0x20
0x39: MOVWF FSR1L
0x3A: CLRF FSR1H
0x3B: MOVF INDF1, W
0x3C: MOVWF 0x79
0x3D: MOVF 0x79, W
0x3E: MOVWF ser_data
!    
!// CLK floating (input) + WPU=ON 
!// pulled low by slave -> interrupt on master
!// master sets DAT to 1 if it wants to transfer data
!// slave waits for CLK to go high
!IO_RA0_serdat0_LAT=ser_data.b1;
0x3F: MOVF ser_data, W
0x40: ANDLW 0x1
0x41: MOVWF 0x79
0x42: RRF 0x79, W
0x43: BTFSS STATUS, 0x0
0x44: GOTO 0x48
0x45: MOVLB 0x2
0x46: BSF PORTA, 0x0
0x47: GOTO 0x4A
0x48: MOVLB 0x2
0x49: BCF PORTA, 0x0
!IO_RA1_serclk0_LAT=1;
0x4A: BSF PORTA, 0x1
!IO_RA0_serdat0_LAT=ser_data.b2;
0x4B: MOVLB 0x0
0x4C: RRF ser_data, W
0x4D: ANDLW 0x1
0x4E: MOVWF 0x79
0x4F: RRF 0x79, W
0x50: BTFSS STATUS, 0x0
0x51: GOTO 0x55
0x52: MOVLB 0x2
0x53: BSF PORTA, 0x0
0x54: GOTO 0x57
0x55: MOVLB 0x2
0x56: BCF PORTA, 0x0
!IO_RA1_serclk0_LAT=0;
0x57: BCF PORTA, 0x1
!IO_RA0_serdat0_LAT=ser_data.b3;
0x58: MOVLB 0x0
0x59: RRF ser_data, W
0x5A: RRF WREG, F
0x5B: ANDLW 0x1
0x5C: MOVWF 0x79
0x5D: RRF 0x79, W
0x5E: BTFSS STATUS, 0x0
0x5F: GOTO 0x63
0x60: MOVLB 0x2
0x61: BSF PORTA, 0x0
0x62: GOTO 0x65
0x63: MOVLB 0x2
0x64: BCF PORTA, 0x0
I will change that to shifting and testing bit0, hope it requires less instructions.
 

Thread Starter

takao21203

Joined Apr 28, 2012
3,702
It is a little bit shorter but not much:

Rich (BB code):
!IO_RA0_serdat0_LAT=ser_data.sd&1;ser_data.sd>>=1;
0x73: MOVLB 0x0
0x74: BTFSS ser_data, 0x0
0x75: GOTO 0x79
0x76: MOVLB 0x2
0x77: BSF PORTA, 0x0
0x78: GOTO 0x7B
0x79: MOVLB 0x2
0x7A: BCF PORTA, 0x0
0x7B: BCF STATUS, 0x0
0x7C: MOVLB 0x0
0x7D: RRF ser_data, F
 

Thread Starter

takao21203

Joined Apr 28, 2012
3,702
When I change it to a pointer variable, I get this:

Rich (BB code):
!void send8bits(unsigned char index)
0x35: MOVLB 0x0
0x36: MOVWF index
!{unsigned char ser_buf;
! unsigned char* ser_data=&ser_buf;
0x37: MOVLW 0x54
0x38: MOVWF 0x79
0x39: MOVF 0x79, W
0x3A: MOVWF ser_data
! *ser_data=spiReadBuffer[index];
0x3B: MOVF index, W
0x3C: ADDLW 0x20
0x3D: MOVWF FSR1L
0x3E: CLRF FSR1H
0x3F: MOVF INDF1, W
0x40: MOVWF 0x79
0x41: MOVF ser_data, W
0x42: MOVWF FSR1L
0x43: CLRF FSR1H
0x44: MOVF 0x79, W
0x45: MOVWF INDF1
!    
!// CLK floating (input) + WPU=ON 
!// pulled low by slave -> interrupt on master
!// master sets DAT to 1 if it wants to transfer data
!// slave waits for CLK to go high
!IO_RA0_serdat0_LAT=(*ser_data)&1;*ser_data>>=1;
0x46: MOVF ser_data, W
0x47: MOVWF FSR1L
0x48: CLRF FSR1H
0x49: BTFSS INDF1, 0x0
0x4A: GOTO 0x4E
0x4B: MOVLB 0x2
0x4C: BSF PORTA, 0x0
0x4D: GOTO 0x50
0x4E: MOVLB 0x2
0x4F: BCF PORTA, 0x0
0x50: MOVLB 0x0
0x51: MOVF ser_data, W
0x52: MOVWF FSR1L
0x53: CLRF FSR1H
0x54: BCF STATUS, 0x0
0x55: RRF INDF1, F
!IO_RA1_serclk0_LAT=1;
0x56: MOVLB 0x2
0x57: BSF PORTA, 0x1
!/*IO_RA0_serdat0_LAT=*ser_data&1;ser_data.sd>>=1;
!IO_RA1_serclk0_LAT=0;
It is lengthy because the generated code is reloading the FSRL all the time + also clearing FSRH.

The correct value for FSRL is already set when I assign the address of the char to the pointer variable.

Knowing the FSRL is set, I could use it in an assembler sequence, without the need for the MOVLB statements (leave permanently at 2 for PORTX), without the need to reload FSRL, and pre-setting the port bit, I only need one branch.

This is the background for the post- to get some advice what is best.
 
Top