uraimo / SwiftyGPIO

A Swift library for hardware projects on Linux/ARM boards with support for GPIOs/SPI/I2C/PWM/UART/1Wire.
MIT License
1.35k stars 135 forks source link

UART example exception #54

Open AleyRobotics opened 7 years ago

AleyRobotics commented 7 years ago

Board Type

RaspberryPi3

Operating System

Linux version 4.9.24-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611) ) #993 SMP Wed Apr 26 18:01:23 BST 2017

Swift Version

Swift version 3.0.2-dev (LLVM 976c51bb80, Clang 18b736028f, Swift c907b76555) Target: armv7--linux-gnueabihf Myself compile from https://github.com/swift-arm/build-swift

Description

Have connected USB-UART cp1202 to USB. TX connected to RX (loop back) project source SwiftyGPIO/Examples/UART/raspi2/

MESSAGE: Send: fatal error: Index out of range: file /home/pi/buildSwiftOnARM/build-swift/scripts/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 364

uraimo commented 7 years ago

I'm looking into this, but no idea what could be the the cause for now, the subscripts in UART.swift look ok. It never happened with devices where I was only reading data, weird (and it's not replicable crossing the uart pins but only with an adapter from a pc).

3Qax commented 5 years ago

I got the same error using I2C

Board Type RaspberryPiZero v1.3

Operating System Raspbian GNU/Linux 9 (stretch)

Swift Version Swift version 5.0 (swift-5.0-RELEASE) Target: armv6-unknown-linux-gnueabihf Downloaded compiled binary.

Description I am playing around with SSD1306. I want to send data buffer array from RPi to display over i2c. I get this crash when trying to send whole or large chunks of array like:

print("Buffer count: \(buffer.count)")
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[0...127]))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[128...255]))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[256...383]))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[384...511]))

or

i2c.writeData(self.address, command: 0b01000000, values: buffer)

Note that when sending data byte by byte like:

for pageColumn in buffer {
    i2c.writeByte(self.address, command: 0b01000000, value: pageColumn)
}

everything works.

MESSAGE: pi@raspberrypi:~/test $ swift run Buffer count: 512 Fatal error: Index out of range: file /home/pi/buildSwiftOnARM/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 369 Current stack trace: Illegal instruction

uraimo commented 5 years ago

Hmm, the error is the same but the issue is specific of I2C (for the former one, swiftygpio didn't even touch the data array, could have been fixed by Swift since 3.0.2).

@3Qax the commit f027a550f970c28e05b94ae869ddbb52665d5e4c should fix it (tagged as 1.1.4), not only because if fixes a check that clearly didn't work for arrays bigger than the de-facto default payload size of 32 but also because writeData now sends whatever the user asks ignoring that artificial limit. Can you check if this fixes your issue? Thanks!

3Qax commented 5 years ago

@uraimo when using 1.1.4 and trying to:

var buffer = Array<UInt8>(repeating: 0, count: 128*(32/8))
i2c.writeData(self.address, command: 0b01000000, values: buffer)

The result is

pi@raspberrypi:~/SwiftyOLED $ swift run
Fatal error: Not enough bits to represent the passed value: file /home/pi/buildSwiftOnARM/swift/stdlib/public/core/Integers.swift, line 3371
Current stack trace:
Illegal instruction

I think the issue is https://github.com/uraimo/SwiftyGPIO/blob/f027a550f970c28e05b94ae869ddbb52665d5e4c/Sources/I2C.swift#L421 because in this case the count() of buffer is equal to 512, which is too big to be represented as UInt8.

Let's try sending it by smaller chunks:

var buffer = Array<UInt8>(repeating: 0, count: 128*(32/8))
print(type(of: Array(buffer[0...127])))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[0...127]))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[128...255]))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[256...383]))
i2c.writeData(self.address, command: 0b01000000, values: Array(buffer[384...511]))

Unfortunately this also doesn't work:

pi@raspberrypi:~/SwiftyOLED $ swift run
Array<UInt8>
I2C write failed: Invalid argument
Aborted

Which makes no sens to me.

uraimo commented 5 years ago

Two different issues, for the first one yeah, that data[0] contains the buffer length to be sent to the lower layers, so the hard limit is 255. That "invalid argument" comes right from the I2C driver that has likely detected an invalid content in the buffer. Since I don't have an SD1306 right now, could you try sending 64 bytes chunks and see if the second example works?

3Qax commented 5 years ago

I tried it and it is still aborting with the same message. Even when sending only a array with count of 63 to leave room for command.

uraimo commented 5 years ago

Hmm, I've reworked a bit the previous commit but that shouldn't change anything for you. Also, that invalid argument error could have multiple causes.

I'm looking at the SSD1306 library from Adafruit, did all the commands before that needed (I guess) to configure the display work without errors?

3Qax commented 5 years ago

Yes, in fact I was also looking at Adafruit library as a reference.

uraimo commented 5 years ago

Correct me if I'm wrong, but they control their send rate using a WIRE_MAX constant that could reasonably be 32 and they break and restart the I2C connection every WIRE_MAX bytes here.

Edit: And when they use ssd1306_command1 they just send a single byte, the bulk update for the screen content seems to be performed with ssd1306_commandList.

Now that the array size issue should be fixed, can you verify if limiting everything to 32 byte chunks you see any improvements? (sorry if I keep asking to try stuff this way...I'm checking if I have one of these displays in a box somewhere)

3Qax commented 5 years ago

I had to make sure that whatever I pass as values parameter it's count can not be higher than 31. Otherwise I got invalid argument error. Yes, 31 - I think that command is counting too to that 32 Bytes. But when doing so, by for example:

var i = 0
while i <= buffer.count {
        i2c.writeData(self.address, command: 0b01000000, values: Array(buffer.dropFirst(i).prefix(31)))
        i += 31
 }

It is outputting some weird data that I don't get when using writeByte, which results in data not being correctly displayed. Everything is shifted. Especially at the beginning. Also these strange line are appearing which I think is some data different than 0. Are you sure that you are not outputting that values.count as a first element in array ? IMG_3595 When using previous way of outputting data it looked like: IMG_3596

uraimo commented 5 years ago

I'm trying your code with a 0xAA pattern that should display some lines but they get broken up at regular intervals. For me, sending 32 bytes works too (better in fact).

The initial count should be read by the driver and altering it sometimes gives invalid payload errors depending on the values. I've seen a few weird things in the Adafruit lib, like the display packets starting with a 0x40 (tried, didn't work).

Could it be that some section of the display configuration is still wrong/missing? The initial setup seems identical to the one from Adafruit (with a missing disableScrolling that doesn't seem to change anything and the 3 instead of 0xFF) but maybe there are still additional steps missing when you send the buffer as a stream.

3Qax commented 5 years ago

About starting display packets with 0x40. Take a look at datasheet page 20 ( Write mode for I2 C). Them sending 0x40 is exacly the same thing as me starting with 0b01000000. This is control data package. It tells driver how next data package should be interpreted, in this case as display data.

uraimo commented 5 years ago

Oh... right, nvm. I have no ideas right now about what could be wrong other than setting specific to the display, I'll play around with it writing blocks of increasing sizes to see if I can get a better idea of what's happening. In my last tests there seemed to be just a 1 bit alignment error somewhere that resulted in a missing pixel in my series of lines (sending 33bytes, 1 len+32 data).

Fishybob commented 5 years ago

Hi Uraimo,

Could I2C_SMBUS_I2C_BLOCK_DATA (#define 8) in place of I2C_SMBUS_BLOCK_DATA (#define 5) get ioctl() to correctly extract the data byte count that was inserted as the first byte of the data buffer?

Thanks, Scott.

https://raspberrypi.stackexchange.com/questions/41316/i2c-using-wiringpi-or-ioctl-in-c-more-than-2-bytes/41323

"The actual length is to be set as the first byte of data. Data should contain "count" followed by "count" bytes of data. Using I2C_SMBUS_I2C_BLOCK_DATA, only the data bytes are sent. Using I2C_SMBUS_BLOCK_DATA, the whole bunch is sent : the "count" byte then all data."

Fishybob commented 5 years ago

I just confirmed that the above modification in I2C.swift cleared up the extra byte (buffer length) that was showing up on my I2C OLED display...

uraimo commented 5 years ago

Thanks for debugging this @Fishybob! I've added two new read/write methods and pushed 1.1.6, still need to test it though.

uraimo commented 5 years ago

Ok, the display works for me too using the new i2c_block_data method. I've also reverted the modifications related to the max packet size bringing it back to a max of 32 bytes of actual data (for both methods, no invalid arg errors), bigger blocks will have to be broken down to smaller segment as we were already doing.

3Qax commented 5 years ago

I just checked it and using readI2CData(_ address: Int, command: UInt8) solves any issues. A big thanks for all of You ❤️

uraimo commented 5 years ago

Great! I'll keep an eye on the progress of your library ;)