Facebook Embed

12 October 2015

ILI9341 SPI Display on RISCOS Pico

It has been a fun mini-adventure to make the SPI LCD display work nicely on RISCOS...


There is a third-party module available to do communications on RISCOS, called PiSPI, but it is very basic and it takes more than a second to send all the data for refreshing the screen once. This effort started because I wanted to progress to investigating using the DMA engine on the Raspberry Pi, which is why the variable dmabuffer% contains the data in a format ready to be sent via the DMA engine. In any case, there is a lot of RAM available on the Raspberry Pi.

What is very convenient on RISCOS is the OS_SpriteOp system calls, which support creating sprites in many bit depths and sizes. It supports the 16bit format used by the display. Unfortunately, there is no call to rotate sprites and because the display is a 240x320 portrait display and I wanted to use it as if it was a 320x240 landscape display, I had to do that transformation for myself.

A few notes about this implementation:

  • GPIO25 is attached to the command/data pin of the display.
  • GPIO24 is attached to the reset pin of the display.
  • The SPI pins are attached as one would expect.
In spite of being "PIO" mode, performance is respectable.




Since this now works "good enough", I will proceed with other parts of my project and I may return to it when I need to get the DMA stuff working... Most likely because I'd like to find ways to reduce power consumption.
   10 REM >DMA9341
   20 
   30 spiclk%=4
   40 
   50 SYS "OS_SWINumberFromString",,"OS_SpriteOp" TO spriteOp%
   60 SYS "OS_SWINumberFromString",,"ColourTrans_SetGCOL" TO setGCOL%
   70 SYS "OS_SWINumberFromString",,"ColourTrans_ReturnColourNumber" TO getColourNumber%
   80 
   90 DIM vdurestore%(3)
  100 
  110 DIM code% 4096
  120 PRINT "Allocating scratchpad at ";~code%
  130 
  140 REM 320 * 240 * 16bit + 44 sprite header + 16 area header = 1536460
  150 DIM spritearea% 153660
  160 
  170 REM 320 * 240 * 16bit * 32bit words = 614400
  180 DIM dmabuffer% 614400
  190 
  200 spritearea%!0=153660
  210 spritearea%!4=0
  220 spritearea%!8=16
  230 spritearea%!12=16
  240 
  250 PRINT "Initializing sprite area at ";~spritearea%
  260 SYS spriteOp%,256+15,spritearea%,"lcd",0,320,240,&78a04001
  270 spriteptr%=spritearea%+16
  280 
  290 PRINT "Initializing sprite at ";~spriteptr%
  300 SYS spriteOp%,512+62,spritearea%,spriteptr% TO sarsize%
  310 
  320 DIM savearea% sarsize%
  330 PRINT "Initializing savearea at ";~savearea%
  340 savearea%!0=0
  350 
  360 SYS "OS_Memory",13,&20200000,&100 TO ,,,gpio%
  370 PRINT "GPIO mapped at ";~gpio%
  380 SYS "OS_Memory",13,&20204000,&100 TO ,,,spi%
  390 PRINT "SPI mapped at ";~spi%
  400 
  410 PROCinitGPIO
  420 
  430 DIM spiSendCode% 1024
  440 FOR pass%=0 TO 2 STEP 2
  450   P%=spiSendCode% : [ OPT pass%
  460   .spiSend
  470   SWI   "OS_EnterOS"
  480   ; SWI   256+65
  490   LDR   R3,gpiov
  500   MOV   R4,#1
  510   MOV   R2,R4,LSL #25
  520   STR   R2,[R3,#40]
  530   MCR   cp15,0,R3,c7,c10,5
  540   :
  550   LDR   R3,spiv
  560   LDR   R2,[R3,#0]
  570   AND   R2,R2,#&0c
  580   ORR   R2,R2,#&6b0
  590   STR   R2,[R3,#0]
  600   :
  610   MCR   cp15,0,R3,c7,c10,5
  620   STR   R0,[R3,#4]
  630   :
  640   .cmdwait
  650   MCR   cp15,0,R3,c7,c10,5
  660   LDR   R2,[R3,#0]
  670   TST   R2,#1<<16
  680   BEQ   cmdwait
  690   ; SWI   256+66
  700   TST   R2,#1<<17
  710   LDRNE R0,[R3,#4]
  720   ;ORR   R2,R2,#&30
  730   ;STR   R2,[R3,#0]
  740   :
  750   LDR   R3,gpiov
  760   MOV   R4,#1
  770   MOV   R2,R4,LSL #25
  780   STR   R2,[R3,#28]
  790   MCR   cp15,0,R3,c7,c10,5
  800   :
  810   LDR   R3,spiv
  820   :
  830   .isdone
  840   MOV   R0,R1
  850   ADD   R0,R0,#64
  860   CMP   R0,R5
  870   BHI   sendrem
  880   :
  890   ;SWI   256+67
  900   LDR   R0,[R1],#4
  910   STR   R0,[R3,#4]
  920   LDR   R0,[R1],#4
  930   STR   R0,[R3,#4]
  940   LDR   R0,[R1],#4
  950   STR   R0,[R3,#4]
  960   LDR   R0,[R1],#4
  970   STR   R0,[R3,#4]
  980   :
  990   LDR   R0,[R1],#4
 1000   STR   R0,[R3,#4]
 1010   LDR   R0,[R1],#4
 1020   STR   R0,[R3,#4]
 1030   LDR   R0,[R1],#4
 1040   STR   R0,[R3,#4]
 1050   LDR   R0,[R1],#4
 1060   STR   R0,[R3,#4]
 1070   :
 1080   LDR   R0,[R1],#4
 1090   STR   R0,[R3,#4]
 1100   LDR   R0,[R1],#4
 1110   STR   R0,[R3,#4]
 1120   LDR   R0,[R1],#4
 1130   STR   R0,[R3,#4]
 1140   LDR   R0,[R1],#4
 1150   STR   R0,[R3,#4]
 1160   :
 1170   LDR   R0,[R1],#4
 1180   STR   R0,[R3,#4]
 1190   LDR   R0,[R1],#4
 1200   STR   R0,[R3,#4]
 1210   LDR   R0,[R1],#4
 1220   STR   R0,[R3,#4]
 1230   LDR   R0,[R1],#4
 1240   STR   R0,[R3,#4]
 1250   :
 1260   CMP   R1,R5
 1270   BEQ   endsend
 1280   :
 1290   .bulk
 1300   ; SWI   256+72
 1310   MCR   cp15,0,R3,c7,c10,5
 1320   .busyloop
 1330   LDR   R2,[R3,#0]
 1340   TST   R2,#1<<16
 1350   BNE   isdone
 1360   TST   R2,#1<<19
 1370   ;MCREQ cp15,0,R3,c7,c0,4 ;wait for interrupt
 1380   BEQ   busyloop
 1390   :
 1400   ; SWI   256+73
 1410   MOV   R0,R1
 1420   ADD   R0,R0,#48
 1430   CMP   R0,R5
 1440   BHI   sendrem
 1450   :
 1460   ; SWI   256+74
 1470   LDR   R2,[R3,#4]
 1480   LDR   R0,[R1],#4
 1490   STR   R0,[R3,#4]
 1500   LDR   R2,[R3,#4]
 1510   LDR   R0,[R1],#4
 1520   STR   R0,[R3,#4]
 1530   LDR   R2,[R3,#4]
 1540   LDR   R0,[R1],#4
 1550   STR   R0,[R3,#4]
 1560   LDR   R2,[R3,#4]
 1570   LDR   R0,[R1],#4
 1580   STR   R0,[R3,#4]
 1590   :
 1600   LDR   R2,[R3,#4]
 1610   LDR   R0,[R1],#4
 1620   STR   R0,[R3,#4]
 1630   LDR   R2,[R3,#4]
 1640   LDR   R0,[R1],#4
 1650   STR   R0,[R3,#4]
 1660   LDR   R2,[R3,#4]
 1670   LDR   R0,[R1],#4
 1680   STR   R0,[R3,#4]
 1690   LDR   R2,[R3,#4]
 1700   LDR   R0,[R1],#4
 1710   STR   R0,[R3,#4]
 1720   :
 1730   LDR   R2,[R3,#4]
 1740   LDR   R0,[R1],#4
 1750   STR   R0,[R3,#4]
 1760   LDR   R2,[R3,#4]
 1770   LDR   R0,[R1],#4
 1780   STR   R0,[R3,#4]
 1790   LDR   R2,[R3,#4]
 1800   LDR   R0,[R1],#4
 1810   STR   R0,[R3,#4]
 1820   LDR   R2,[R3,#4]
 1830   LDR   R0,[R1],#4
 1840   STR   R0,[R3,#4]
 1850   :
 1860   CMP   R1,R5
 1870   BNE   bulk
 1880   :
 1890   .endsend
 1900   ; SWI   256+69
 1910   .endloop
 1920   MCR   CP15,0,R3,c7,c10,5
 1930   LDR   R2,[R3,#0]
 1940   TST   R2,#1<<17
 1950   LDRNE R0,[R3,#4]
 1960   TST   R2,#1<<16
 1970   BEQ   endloop
 1980   :
 1990   ORR   R2,R2,#&30
 2000   BIC   R2,R2,#&80
 2010   STR   R2,[R3,#0]
 2020   :
 2030   SWI   "OS_LeaveOS"
 2040   :
 2050   MOV   PC,R14
 2060   :
 2070   .sendrem
 2080   ; SWI   256+68
 2090   CMP   R1,R5
 2100   BEQ   endsend
 2110   LDR   R0,[R1],#4
 2120   STR   R0,[R3,#4]
 2130   BAL   sendrem
 2140   :
 2150   .gpiov
 2160   EQUD  gpio%
 2170   .spiv
 2180   EQUD  spi%
 2190   .SPI_AND_MASK
 2200   EQUD  &0000000c
 2210   .SPI_OR_MASK
 2220   EQUD  &000006b0
 2230   :
 2240   
 2250   .lineblt
 2260   LDR   R1,[R0],#4
 2270   MOV   R4,R1,LSR #8
 2280   AND   R3,R1,#&ff
 2290   AND   R4,R4,#&ff
 2300   STR   R4,[R2],#4
 2310   STR   R3,[R2],#-&784
 2320   MOV   R4,R1,LSR #24
 2330   MOV   R3,R1,LSR #16
 2340   AND   R4,R4,#&ff
 2350   AND   R3,R3,#&ff
 2360   STR   R4,[R2],#4
 2370   STR   R3,[R2],#-&784
 2380   CMP   R5,R2
 2390   MOVHI PC,R14
 2400   BAL   lineblt
 2410   :
 2420 ] : NEXT pass%
 2430 
 2440 PRINT "Resetting LCD display"
 2450 PROClcdReset
 2460 PRINT "Initializing LCD display"
 2470 PROClcdSetup
 2480 PRINT "Performing first update"
 2490 start%=TIME:PROClcdUpdate:PRINT "Took ";(TIME-start%)/100;" seconds"
 2500 REM PRINT "again"
 2510 REM PROClcdUpdate
 2520 REM END
 2530 PRINT "Running PROCtest"
 2540 PROCtest
 2550 
 2560 END
 2570 
 2580 DEF PROCspi(cmd%,addr%,end%)
 2590 REM PRINT "PROCspi(&";~cmd%;",&";~addr%;",&";~end%;")"
 2600 A%=cmd%:B%=addr%:F%=end%
 2610 CALL spiSend
 2620 ENDPROC
 2630 
 2640 DEF PROClcdUpdate
 2650 LOCAL y%
 2660 A%=spriteptr%+44
 2670 FOR y%=dmabuffer% TO dmabuffer%+&778 STEP 8
 2680   C%=y%+&95880:F%=y%:A%=USR lineblt
 2690 NEXT y%
 2700 PROCspi(&2c,dmabuffer%,dmabuffer%+614400)
 2710 ENDPROC
 2720 
 2730 DEF PROCsetGCOL(rgb%)
 2740 SYS setGCOL%,rgb%,,,0,3
 2750 ENDPROC
 2760 
 2770 DEF PROCsetColour(rgb%)
 2780 LOCAL colour%
 2790 SYS getColourNumber,rgb% TO colour%
 2800 COLOUR colour%
 2810 ENDPROC
 2820 
 2830 DEF PROCtest2
 2840 LOCAL x%,y%,p%
 2850 p%=spriteptr%+44
 2860 FOR y%=0 TO 239
 2870   FOR x%=0 TO 319
 2880     IF (x% AND &4)=(y% AND &4) THEN
 2890       p%?0 = &ff
 2900       p%?1 = &ff
 2910       ELSE
 2920       p%?0 = &00
 2930       p%?1 = &00
 2940     ENDIF
 2950     p%=p%+2
 2960   NEXT x%
 2970 NEXT y%
 2980 PROClcdUpdate
 2990 ENDPROC
 3000 
 3010 DEF PROCtest
 3020 LOCAL start%,end%,c%
 3030 PROCvduLcdSprite
 3040 
 3050 PRINT "Hello World from RISCOS"
 3060 start%=TIME:PROClcdUpdate:end%=TIME
 3070 PRINT
 3080 
 3090 PRINT "frame refresh takes ";(end%-start%)/100;" seconds"
 3100 
 3110 FOR c%=0 TO 63
 3120   COLOUR c%
 3130   PRINT "*";
 3140 NEXT c%
 3150 PRINT:PRINT
 3160 
 3170 PROCvduRestore
 3180 PROClcdUpdate
 3190 ENDPROC
 3200 
 3210 DEF PROCvduLcdSprite
 3220 IF vdurestore%(0) THEN
 3230   ERR 6,"Cannot switch"
 3240   ELSE
 3250   SYS spriteOp%,60+512,spritearea%,spriteptr%,savearea% TO vdurestore%(0),vdurestore%(1),vdurestore%(2),vdurestore%(3)
 3260 ENDIF
 3270 ENDPROC
 3280 
 3290 DEF PROCvduRestore
 3300 IF vdurestore%(0) THEN
 3310   SYS spriteOp%,vdurestore%(0),vdurestore%(1),vdurestore%(2),vdurestore%(3)
 3320   vdurestore%(0)=0
 3330   ELSE
 3340   ERR 6,"Cannot restore"
 3350 ENDIF
 3360 ENDPROC
 3370 
 3380 DEF PROCinitGPIO
 3390 LOCAL pass%
 3400 FOR pass%=0 TO 2 STEP 2
 3410   P%=code% : [ OPT pass%
 3420   .start
 3430   SWI   "OS_EnterOS"
 3440   LDR   R0, GPSEL0_OR_MASK
 3450   LDR   R2,[R1,#0]
 3460   ORR   R2,R2,R0
 3470   LDR   R0, GPSEL0_AND_MASK
 3480   AND   R2,R2,R0
 3490   STR   R2,[R1,#0]
 3500   LDR   R0,GPSEL1_OR_MASK
 3510   LDR   R2,[R1,#4]
 3520   ORR   R2,R2,R0
 3530   LDR   R0,GPSEL1_AND_MASK
 3540   AND   R2,R2,R0
 3550   STR   R2,[R1,#4]
 3560   ;LDR   R0,GPSEL2_OR_MASK
 3570   ;LDR   R2,[R1,#8]
 3580   ;ORR   R2,R2,R0
 3590   ;LDR   R0,GPSEL2_AND_MASK
 3600   ;AND   R2,R2,R0
 3610   ;STR   R2,[R1,#8]
 3620   MOV   R0,#%11<<6
 3630   STR   R0,[R1,#&1c]
 3640   MOV   R0,#%111<<9
 3650   STR   R0,[R1,#&28]
 3660   MOV   R0,#&3c
 3670   STR   R0,[R3,#0]
 3680   MOV   R0,#spiclk%
 3690   STR   R0,[R3,#&8]
 3700   SWI   "OS_LeaveOS"
 3710   MOV   PC,R14
 3720   :
 3730   .GPSEL0_OR_MASK
 3740   EQUD  &24800000
 3750   :
 3760   .GPSEL0_AND_MASK
 3770   EQUD  &249fffff
 3780   :
 3790   .GPSEL1_OR_MASK
 3800   EQUD  &00000024
 3810   :
 3820   .GPSEL1_AND_MASK
 3830   EQUD  &2fffffe4
 3840   :
 3850   .GPSEL2_OR_MASK
 3860   EQUD  &00024000
 3870   :
 3880   .GPSEL2_AND_MASK
 3890   EQUD  &fffc0fff
 3900 ] : NEXT pass%
 3910 B%=gpio%:D%=spi%
 3920 CALL start
 3930 SYS "GPIO_WriteMode",24,1
 3940 SYS "GPIO_WriteMode",25,1
 3950 ENDPROC
 3960 
 3970 DEF PROClcdSetup
 3980 LOCAL DATA
 3990 RESTORE +0
 4000 DATA &1ef,&03,&80,&02
 4010 DATA &1cf,&00,&c1,&30
 4020 DATA &1ed,&64,&03,&12,&81
 4030 DATA &1e8,&85,&00,&78
 4040 DATA &1cb,&39,&2c,&00,&34,&02
 4050 DATA &1f7,&20
 4060 DATA &1ea,&00,&00
 4070 DATA &1c0,&23
 4080 DATA &1c1,&10
 4090 DATA &1c5,&3e,&28
 4100 DATA &1c7,&86
 4110 DATA &136,&48
 4120 DATA &13a,&55
 4130 DATA &1b1,&00,&18
 4140 DATA &1b6,&08,&82,&27
 4150 DATA &1f2,&00
 4160 DATA &126,&01
 4170 DATA &1e0,&0f,&31,&2b,&0c,&0e,&08,&4e,&f1,&37,&07,&10,&03,&0e,&09,&00
 4180 DATA &1e1,&00,&0e,&14,&03,&11,&07,&31,&c1,&48,&08,&0f,&0c,&31,&36,&0f
 4190 DATA &111
 4200 DATA -1
 4210 PROClcdSendInitData
 4220 PROCsleep(0.120)
 4230 PROCspi(&29,code%,code%)
 4240 RESTORE DATA
 4250 ENDPROC                                                                  :
 4260 :
 4270 DEF PROClcdSendInitData
 4280 LOCAL ptr%
 4290 ptr%=code%
 4300 READ !ptr%
 4310 WHILE -1<!ptr%
 4320   !ptr%=&ff AND !ptr%
 4330   ptr%=ptr%+4
 4340   READ !ptr%
 4350   WHILE (&100 AND !ptr%)=0
 4360     ptr%=ptr%+4
 4370     READ !ptr%
 4380   ENDWHILE
 4390   PROCspi(!code%,code%+4,ptr%)
 4400   !code%=!ptr%
 4410   ptr%=code%
 4420 ENDWHILE
 4430 ENDPROC
 4440 :
 4450 DEF PROCsleep(d)
 4460 ptr%=d*100+TIME:REPEAT:WAIT:UNTIL TIME>=ptr%
 4470 ENDPROC
 4480 :
 4490 DEF PROClcdReset
 4500 SYS "GPIO_WriteData",24,1
 4510 PROCsleep(0.005)
 4520 SYS "GPIO_WriteData",24,0
 4530 PROCsleep(0.020)
 4540 SYS "GPIO_WriteData",24,1
 4550 PROCsleep(0.150)
 4560 ENDPROC