-
Notifications
You must be signed in to change notification settings - Fork 230
ILI9341: add SPI driver for STM32F4 #196
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
base: dev
Are you sure you want to change the base?
Changes from all commits
ab01269
4a2d86c
30bf08a
dc5d271
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// +build feather_stm32f405 | ||
|
||
package main | ||
|
||
import ( | ||
"machine" | ||
|
||
"tinygo.org/x/drivers/ili9341" | ||
) | ||
|
||
var ( | ||
csPin = machine.D12 | ||
dcPin = machine.D10 | ||
display = ili9341.NewSPI( | ||
machine.SPI0, | ||
dcPin, | ||
csPin, | ||
machine.D8, // if wired to 3.3V, pick an unused pin | ||
) | ||
|
||
// ILI9341's LED pin. set this to an unused pin (but not NoPin!) if | ||
// wired via resistor straight to 3.3V. the boing example tries to | ||
// set this pin and will panic if NoPin is used. | ||
backlight = machine.D13 | ||
) | ||
|
||
func init() { | ||
machine.SPI0.Configure(machine.SPIConfig{ | ||
SCK: machine.SPI0_SCK_PIN, | ||
SDO: machine.SPI0_SDO_PIN, | ||
SDI: machine.SPI0_SDI_PIN, | ||
Frequency: 40000000, | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,6 +58,7 @@ func main() { | |
backlight.High() | ||
|
||
display.SetRotation(ili9341.Rotation270) | ||
time.Sleep(50 * time.Millisecond) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reasons for the addition? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately I don't have any documentation to justify this, only a use case: Before the boing demo enters its main loop (drawing the ball frames), it first draws the entire 1-bit background across the screen. With my Feather, the top ~0.5cm of this background (including the top of the grid) was not being drawn, and would remain either white or have a scrambled gray coloring -- it seemed like the first SPI transactions were starting too quickly and the screen wasn't able to draw the data in time. The rest of the graphics continued normally without any problem. After adding this short delay, I never saw the problem again. I'll try to recreate and take a screenshot. |
||
DrawBackground() | ||
|
||
startTime = time.Now().UnixNano() | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,142 @@ | ||||||||||||||||||||||||||
// +build stm32f4 | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
package ili9341 | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||
"device/stm32" | ||||||||||||||||||||||||||
"machine" | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
type spiDriver struct { | ||||||||||||||||||||||||||
bus machine.SPI | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device { | ||||||||||||||||||||||||||
return &Device{ | ||||||||||||||||||||||||||
dc: dc, | ||||||||||||||||||||||||||
cs: cs, | ||||||||||||||||||||||||||
rst: rst, | ||||||||||||||||||||||||||
rd: machine.NoPin, | ||||||||||||||||||||||||||
driver: &spiDriver{ | ||||||||||||||||||||||||||
bus: bus, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func (pd *spiDriver) configure(config *Config) { | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func (pd *spiDriver) write8(b byte) { | ||||||||||||||||||||||||||
if !pd.bus.Bus.CR1.HasBits(stm32.SPI_CR1_SPE) { | ||||||||||||||||||||||||||
pd.bus.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.setWord(b, true, true) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Comment on lines
+29
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The processing for Any reason to use
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't sure about that either, perhaps it clears certain flags (e.g., overrun/underrun?) in the SPI peripheral once the transaction has completed? You wouldn't want them to persist across transactions. But for good connections with a well-behaving slave, you can probably get by without doing it. Note that the motivation for this disabling and reenabling the peripheral comes from STM32CubeIDE-generated driver code, which also performs this set/clear on CR1 SPE in its Transmit/Receive functions - both their HAL driver and their LL driver do this. For reference, here is my STM32CubeIDE project configured for the STM32F405 Feather. It has UART, I2C, and SPI interfaces all configured on their correct pins, and the system clock is configured with the 12 MHz HSE crystal onboard the feather. @sago35 https://github.com/ardnew/ardnew/tree/master/share/tinygo/sago35 There are two different copies of the exact same project, one using the generated HAL drivers and one using the generated LL (low-level) drivers. You will find the relevant driver code for both in a subdirectory. SPI, for example:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest removing this logic from the ILI9341 driver as you're implying. This logic Do you agree? Or would you remove it from both locations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, it should be removed from ILI9341. |
||||||||||||||||||||||||||
func (pd *spiDriver) write8n(b byte, n int) { | ||||||||||||||||||||||||||
if !pd.bus.Bus.CR1.HasBits(stm32.SPI_CR1_SPE) { | ||||||||||||||||||||||||||
pd.bus.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
for i := 0; i < n; i++ { | ||||||||||||||||||||||||||
pd.setWord(b, i == 0, i+1 == n) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func (pd *spiDriver) write8sl(b []byte) { | ||||||||||||||||||||||||||
if !pd.bus.Bus.CR1.HasBits(stm32.SPI_CR1_SPE) { | ||||||||||||||||||||||||||
pd.bus.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
for i, w := range b { | ||||||||||||||||||||||||||
pd.setWord(w, i == 0, i+1 == len(b)) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func (pd *spiDriver) write16(data uint16) { | ||||||||||||||||||||||||||
if !pd.bus.Bus.CR1.HasBits(stm32.SPI_CR1_SPE) { | ||||||||||||||||||||||||||
pd.bus.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.setWord(uint8(data>>8), true, false) | ||||||||||||||||||||||||||
pd.setWord(uint8(data), false, true) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func (pd *spiDriver) write16n(data uint16, n int) { | ||||||||||||||||||||||||||
if !pd.bus.Bus.CR1.HasBits(stm32.SPI_CR1_SPE) { | ||||||||||||||||||||||||||
pd.bus.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
for i := 0; i < n; i++ { | ||||||||||||||||||||||||||
pd.setWord(uint8(data>>8), i == 0, false) | ||||||||||||||||||||||||||
pd.setWord(uint8(data), false, i+1 == n) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func (pd *spiDriver) write16sl(data []uint16) { | ||||||||||||||||||||||||||
if !pd.bus.Bus.CR1.HasBits(stm32.SPI_CR1_SPE) { | ||||||||||||||||||||||||||
pd.bus.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
for i, w := range data { | ||||||||||||||||||||||||||
pd.setWord(uint8(w>>8), i == 0, false) | ||||||||||||||||||||||||||
pd.setWord(uint8(w), false, i+1 == len(data)) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// puts a single 8-bit word in the SPI data register (DR). | ||||||||||||||||||||||||||
// if first (first word being transmitted) is false, waits for the SPI transmit | ||||||||||||||||||||||||||
// buffer empty bit (TXE) is set before putting the word in DR. | ||||||||||||||||||||||||||
// if last (last word being transmitted) is true, waits for the SPI transmit | ||||||||||||||||||||||||||
// buffer empty bit (TXE) is set and SPI bus busy bit (BSY) is clear before | ||||||||||||||||||||||||||
// returning. | ||||||||||||||||||||||||||
// for all wait operations, a fixed number of wait iterations (const tryMax) are | ||||||||||||||||||||||||||
// performed before a timeout is assumed. | ||||||||||||||||||||||||||
// if timeout occurs, returns false. otherwise, returns true. | ||||||||||||||||||||||||||
func (pd *spiDriver) setWord(word uint8, first bool, last bool) bool { | ||||||||||||||||||||||||||
sago35 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const tryMax = 10000 | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
canWrite := first | ||||||||||||||||||||||||||
for i := 0; (!canWrite) && (i < tryMax); i++ { | ||||||||||||||||||||||||||
canWrite = pd.bus.Bus.SR.HasBits(stm32.SPI_SR_TXE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
if !canWrite { | ||||||||||||||||||||||||||
return false // timeout | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
pd.bus.Bus.DR.Set(uint32(word)) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if last { | ||||||||||||||||||||||||||
canReturn := false | ||||||||||||||||||||||||||
for i := 0; (!canReturn) && (i < tryMax); i++ { | ||||||||||||||||||||||||||
canReturn = pd.bus.Bus.SR.HasBits(stm32.SPI_SR_TXE) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
if !canReturn { | ||||||||||||||||||||||||||
return false // timeout | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
canReturn = false | ||||||||||||||||||||||||||
for i := 0; (!canReturn) && (i < tryMax); i++ { | ||||||||||||||||||||||||||
canReturn = !pd.bus.Bus.SR.HasBits(stm32.SPI_SR_BSY) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
if !canReturn { | ||||||||||||||||||||||||||
return false // timeout | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hadn't noticed.
It is better to have the following changes made.
I think the target source code is as follows.