Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ILI9341 TFT driver (SPI) #153

Merged
merged 11 commits into from
Jun 15, 2020
Merged

Conversation

sago35
Copy link
Member

@sago35 sago35 commented May 20, 2020

This PR adds SPI support for the ILI9341.

@sago35 sago35 changed the title ILI9341 TFT driver (SPI) [WIP] ILI9341 TFT driver (SPI) May 20, 2020
@sago35
Copy link
Member Author

sago35 commented May 20, 2020

@sago35
Copy link
Member Author

sago35 commented May 20, 2020

Below is a commit that adds basic support, but it's too slow.

b8efa55

With pyportal_boing I had the following FPS

  • WioTerminal with spiDriver b8efa55
    • 10fps
  • PyPortal with parallelDriver
    • 51 fps

@sago35
Copy link
Member Author

sago35 commented May 20, 2020

Using 32bit-extension made it a little faster.

  • WioTerminal with spiDriver b8efa55
    • 10 fps
  • WioTerminal with spiDriver + 32bit extension
    • 36 fps
      • Initially, it slows down at 42 fps and then converges to about 36 fps.

note:
tinygo-org/tinygo@658e08b

@sago35
Copy link
Member Author

sago35 commented May 20, 2020

The reason CI is failing is that there is no function for 32bit extensions that I added below.

b8efa55.

@sago35 sago35 changed the title [WIP] ILI9341 TFT driver (SPI) ILI9341 TFT driver (SPI) May 20, 2020
func (pd *spiDriver) configure(config *Config) {
}

//go:inline
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all these //go:inline annotations really improve performance?

In general they should only be added as a fix for performance. Also, before doing that please test with -opt=2 first because that will likely make the //go:inline pragmas unnecessary (and you should add -opt=2 if speed is important).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go:inline is something I copied from parallel_atsamd51.go.
I wasn't particularly aware of it.

Removing go:inline didn't change the speed.
So, I'll remove it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FPS didn't change when I set -opt=2.

@sago35
Copy link
Member Author

sago35 commented May 21, 2020

By using INTEFLAG.DRE, I was able to improve performance without using 32bit-extensions.
ILI9341's pyportal_boing is now 43 fps instead of 36 fps.

There is one area that bothers me.
In the SPI source, the processing speed gets slower and slower with each process.
However, it appears to be converging at 43 fps.

The pyportal parallel_atsamd51.go is similarly slow, but converges from 51 fps to 49 fps.

// pyportal with parallel_atsamd51.go
width, height == 240 320
51  fps
51  fps
51  fps
51  fps
51  fps
49  fps
49  fps
49  fps
50  fps
50  fps
49  fps
49  fps
49  fps
49  fps
49  fps
49  fps
49  fps
49  fps
49  fps
// wioterminal with spi_atsamd51.go
width, height == 240 320
51  fps
46  fps
45  fps
44  fps
44  fps
45  fps
44  fps
44  fps
44  fps
44  fps
44  fps
44  fps
44  fps
44  fps
44  fps
44  fps
43  fps
43  fps
44  fps
44  fps

@sago35
Copy link
Member Author

sago35 commented May 21, 2020

I don't understand why CircleCI failed.
examples/ili9341/basic has pyportal.go and backlight and display should be set, but it's an error.

tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic/main.go
# ./examples/ili9341/basic/main.go
examples/ili9341/basic/main.go:21:2: undeclared name: backlight
examples/ili9341/basic/main.go:23:2: undeclared name: display
examples/ili9341/basic/main.go:24:19: undeclared name: display
examples/ili9341/basic/main.go:26:2: undeclared name: display
examples/ili9341/basic/main.go:27:2: undeclared name: backlight
examples/ili9341/basic/main.go:29:2: undeclared name: display
examples/ili9341/basic/main.go:30:2: undeclared name: display
examples/ili9341/basic/main.go:31:2: undeclared name: display
examples/ili9341/basic/main.go:32:2: undeclared name: display
examples/ili9341/basic/main.go:33:2: undeclared name: display
make: *** [Makefile:60: smoke-test] Error 1

@sago35
Copy link
Member Author

sago35 commented May 24, 2020

TODO: change pin names
Changed.

@deadprogram
Copy link
Member

Looks like the build problem is this:

tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic/main.go
# ./examples/ili9341/basic/main.go
examples/ili9341/basic/main.go:21:2: undeclared name: backlight
examples/ili9341/basic/main.go:23:2: undeclared name: display
examples/ili9341/basic/main.go:24:19: undeclared name: display
examples/ili9341/basic/main.go:26:2: undeclared name: display
examples/ili9341/basic/main.go:27:2: undeclared name: backlight
examples/ili9341/basic/main.go:29:2: undeclared name: display
examples/ili9341/basic/main.go:30:2: undeclared name: display
examples/ili9341/basic/main.go:31:2: undeclared name: display
examples/ili9341/basic/main.go:32:2: undeclared name: display
examples/ili9341/basic/main.go:33:2: undeclared name: display
make: *** [Makefile:60: smoke-test] Error 1

@sago35
Copy link
Member Author

sago35 commented May 27, 2020

Now I know why the CI test was failing.
This is because I specified main.go in go build.
So, it's fixed.
I added a test for pyportal_boing as a side note.

But this time, I got a different error.
Where I made the error is not where I made the change.

#!/bin/bash -eo pipefail
make smoke-test

tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
# tinygo.org/x/drivers/adt7410
examples/adt7410/main.go:8:2: cannot find package "." in:
	/usr/local/go/src/vendor/tinygo.org/x/drivers/adt7410
make: *** [Makefile:12: smoke-test] Error 1

Exited with code exit status 2

@sago35
Copy link
Member Author

sago35 commented May 28, 2020

@deadprogram
The change does not appear to be related to CI error.
Do you have anything to think about?

diff: 2ac3d61

ci before: https://circleci.com/gh/tinygo-org/drivers/468
ci now: https://circleci.com/gh/tinygo-org/drivers/471?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link

@deadprogram
Copy link
Member

This change is now needed in order to run tinygo build on CircleCI: 7967e82

@aykevl might this be related to us now using Go modules? I am OK with making this change for the build, but I think we might want to understand if it might be something to look into further.

@sago35
Copy link
Member Author

sago35 commented Jun 1, 2020

rebased.
smoke-test is now OK.

@aykevl
Copy link
Member

aykevl commented Jun 4, 2020

I think the CI issue was that we were checking out in GOROOT, while it should have been checking out in GOPATH. But now with Go modules, the location doesn't matter much (apart from that it shouldn't be in GOROOT, apparently). See: #157

@sago35
Copy link
Member Author

sago35 commented Jun 6, 2020

rebased.

@deadprogram
Copy link
Member

I have tested on PyPortal and it is noticeably faster. Good work @sago35 thank you!

@aykevl @bgould @conejoninja any last feedback before I squash/merge this?

@sago35
Copy link
Member Author

sago35 commented Jun 6, 2020

wioterminal.go did not need power control, so I removed it.
Other than that, I haven't changed it.

@sago35
Copy link
Member Author

sago35 commented Jun 9, 2020

Removed unnecessary pins in struct.
I think everything else is fine.

@sago35
Copy link
Member Author

sago35 commented Jun 9, 2020

I'm working on another driver for spi with DMA, but I'll be creating a separate PR for it.

  • pyportal / parallel : 49fps
  • wioterminal / spi : 44 fps
  • wioterminal / spi + DMA : 64 fps

The source code is below, but refactoring is required.
sago35@71349ad

@deadprogram
Copy link
Member

Any feedback on this @conejoninja or @bgould ?

Copy link
Member Author

@sago35 sago35 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. (5cd2e05)
Since the actual values were the same, the behavior remains the same.

	SERCOM_SPIS_CTRLB_RXEN        = 0x20000 // Bit RXEN.
	SERCOM_SPIS_INTFLAG_DRE       = 0x1  // Bit DRE.
	SERCOM_SPIS_SYNCBUSY_CTRLB    = 0x4  // Bit CTRLB.


	SERCOM_SPIM_CTRLB_RXEN        = 0x20000 // Bit RXEN.
	SERCOM_SPIM_INTFLAG_DRE       = 0x1  // Bit DRE.
	SERCOM_SPIM_SYNCBUSY_CTRLB    = 0x4  // Bit CTRLB.

}

func (pd *spiDriver) write8(b byte) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIS_CTRLB_RXEN)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's SPIM, not SPIS, so it needs to be fixed

@bgould
Copy link
Member

bgould commented Jun 14, 2020

@deadprogram How are you testing this on pyportal? Did you have to cut and short the traces on the back to switch it into SPI mode, or some other way? The ILI9341 board I have is my pyportal so just curious if I need to do that (I want to try out the DMA improvement when its ready so I don't mind making that change if it is necessary)

@deadprogram
Copy link
Member

No I did not modify my PyPortal. I do have a WioTerminal now that I am using for testing.

@deadprogram
Copy link
Member

@sago35 are you going to add your DMA code to this PR?

@conejoninja
Copy link
Member

Did not test it, but the code looks good to me. Maybe the driver interface should be in a separate file, or at the beginning of ili6341.go (I like to put on top var declarations, structs, and interfaces, not sure if that follows the go guidelines)

@sago35
Copy link
Member Author

sago35 commented Jun 15, 2020

are you going to add your DMA code to this PR?
It is better to keep PR separate.

For DMA support, a wait is required after DMA transfer and before SPI transmission (or CS/DC, etc.).
It may be troublesome to coexist with parallel if the current method of ili9341.go is used.

The following is a great library, and it would be easier to make a good library if you focus on SPI + DMA only, as shown below.
https://github.com/lovyan03/LovyanGFX

@sago35
Copy link
Member Author

sago35 commented Jun 15, 2020

Using DMA in the following way will not change the speed.
You must have another job to do while you are waiting for DMA to complete.

func (pd *spiDriver) write16sl(data []uint16) {
	pd.DmaSend16(data)
	for !sam.DMAC.CHANNEL[pd.dmaChannel].CHINTFLAG.HasBits(sam.DMAC_CHANNEL_CHINTFLAG_TCMPL) {
	}
	sam.DMAC.CHANNEL[pd.dmaChannel].CHINTFLAG.SetBits(sam.DMAC_CHANNEL_CHINTFLAG_TCMPL)
}

@bgould
Copy link
Member

bgould commented Jun 15, 2020

Not really pertinent to this PR, but pretty sure DMA is possible with the parallel interface also - https://www.youtube.com/watch?v=ClabKcWVxT8 - there is C code demonstrating that in the Adafruit ILI9341 driver for Arduino, it is a little hard to read though IMO especially because it is buried inside #ifdefs.

I'm not going modify my pyportal at this time to test, the code looks good to me though. Probably DMA could be handled separately; I imagine that possibly could require a new driver interface - I believe @aykevl might have some ideas about that and also the current work to make blocking operations in TinyGo work properly with interrupts could come into play as well.

Thats all to say that the SPI driver looks good to me and DMA support could be integrated separately IMO. Nice work @sago35 :)

@deadprogram
Copy link
Member

I am going to squash/merge this PR now. Thank you very much @sago35 for another awesome contribution!

@deadprogram deadprogram merged commit 941ea4e into tinygo-org:dev Jun 15, 2020
@sago35
Copy link
Member Author

sago35 commented Jun 16, 2020

https://www.youtube.com/watch?v=ClabKcWVxT8

@bgould
If you know where the source code is, please let me know.
I would like to read it.

My latest try was 136 fps, but adafruit's video is a bit faster than that.

pyportal_boing 136 fps with TinyGo + Wio Terminal
https://www.youtube.com/watch?v=YZ9RTJB9Ac4

@sago35
Copy link
Member Author

sago35 commented Jun 16, 2020

Since DMA is not supposed to be able to do DMA on ports, I guess it's like inverting wr with TC while rewriting RAM with DMA.

@sago35
Copy link
Member Author

sago35 commented Jun 16, 2020

Did you have to cut and short the traces on the back to switch it into SPI mode, or some other way?

I also tried the SPI by shorting out the jumper on the PyPortal board, but the screen did not appear.

It is important to note that TFT_RS and SCK are shorted by the jumper.
I think it is not good to switch the jumper while the parallel driver program is running.
I may have broken the board.

The feather-m4 and the external SPI ILI9341 worked correctly, so I think something is wrong with the settings.

@aykevl
Copy link
Member

aykevl commented Jun 17, 2020

I believe @aykevl might have some ideas about that and also the current work to make blocking operations in TinyGo work properly with interrupts could come into play as well.

My intention is the following:

  • Make regular spi.Tx functions use DMA transparently when available (in a blocking fashion). This can be useful to do the send in one goroutine while the other renders the next frame. This depends on scheduler support that Jaden is working on.
  • In addition to that, to avoid scheduler overhead it may be necessary to also define a "start transaction" function and a way to wait until the last one is sent. That would allow a single goroutine to use DMA to improve performance. But it also complicates things so ideally I'd want to see how bad the first option is before doing this one.

@sago35
Copy link
Member Author

sago35 commented Jun 19, 2020

I tried again, and the SPI driver works on the PyPortal.
As it says on the adafruit page, you need to make two changes.

It is best not to use ILI9341, such as blinky, before working with it.
TFT_RS and SCK will be shorted out.

image
https://www.youtube.com/watch?v=VKO-poNS8wU

@sago35
Copy link
Member Author

sago35 commented Jun 19, 2020

examples/ili9341/pyportal_boing/pyportal.go

// +build pyportal

package main

import (
	"machine"

	"tinygo.org/x/drivers/ili9341"
)

var (
	display = ili9341.NewSpi(
		machine.SPI0,
		machine.TFT_WR, // In SPI mode, WR is used instead of DC.
		machine.TFT_CS,
		//machine.NoPin,
		machine.TFT_RESET,
	)

	backlight = machine.TFT_BACKLIGHT
)

func init() {
	machine.SPI0.Configure(machine.SPIConfig{
		SCK:       machine.SPI0_SCK_PIN,
		MOSI:      machine.SPI0_MOSI_PIN,
		MISO:      machine.SPI0_MISO_PIN,
		LSBFirst:  false,
		Mode:      machine.Mode0,
		Frequency: 40000000,
	})

	machine.D34.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D34.Low()
	machine.D35.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D35.Low()
	machine.D36.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D36.Low()
	machine.D37.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D37.Low()
	machine.D38.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D38.Low()
	machine.D39.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D39.Low()
	machine.D40.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D40.Low()
	machine.D41.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.D41.Low()

	machine.TFT_RD.Configure(machine.PinConfig{Mode: machine.PinOutput})
	machine.TFT_RD.High()

	machine.TFT_DC.Configure(machine.PinConfig{Mode: machine.PinInput})
}

sago35 added a commit to sago35/drivers that referenced this pull request Jul 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants