The imp API provides users of I²C peripheral devices (‘peripherals’) with debugging information following failed attempts to read or write data to any I²C peripheral connected to an imp. This information comes in the form of an integer constant returned by either i2c.write() or i2c.readerror(), depending on the type of operation being performed. If either method returns any value other than 0, an error has occurred. The return value indicates the nature of the error, and these errors are listed in the i2c.readerror() documentation. Should an error occur, the I²C transaction is abandoned.
This document describes these errors in more detail. They are presented in the order in which they are likely to be encountered, with those relating to write operations appearing first, followed by those issued during read operations.
The imp API makes sending and receiving data via I²C very straightforward, but ‘under the hood’ the process as is complex. As such, a single imp API I²C access may result in any one of a series of errors, depending on which stage of the process the underlying impOS™ I²C code has reached.
The I²C Protocol
Developers working with imp005-based devices should note that the following applies primarily to other imp devices. The imp005’s Broadcom SoC will report only two I²C error values: -13 and -9. The former is as described below, but the latter covers almost all of the remaining I²C errors: unfortunately, some error conditions aren’t reported at all (success is falsely indicated). We hope to improve imp005 I²C error reporting in a future version of impOS.
The Errors
Not Enabled (-13)
This is a straightforward error that is issued when you have attempted to access an imp I²C bus that has not yet been configured. Check your code and, if necessary, call i2c.configure() with a supported speed constant passed as a parameter.
Controller Select Error (-1)
The imp signals its intention to begin an I²C transaction by establishing the standard I²C start condition: it attempts to pull the SDA line low (so the waveform has a falling edge) while the SCL line remains high. If the imp is unable to pull SDA low, this error will be issued.
This error may arise if another I²C master is operating on the same bus and has taken control of it. If the imp is the only master on the bus, this error may result from poorly chosen pull-up resistors. I²C ports are open drain so can only pull the SDA and SCL lines low; pull-up resistors are required to drive the lines high when they are released by bus devices.
Transmit Select Error (-2)
After the imp has signalled start and is ready to write data to it, it sends the 7-bit address of the I²C peripheral it wants to communicate with, followed by a single bit indicating whether the transaction is a write (the imp pulls SDA low) or a read (the imp leaves SDA high). These eight bits should be acknowledged by a single-bit ACK signal from the peripheral at the transmitted address; it pulls SDA low. If the acknowledgement doesn’t occur during the ninth clock pulse, then the imp will issue this error.
If this error is encountered, check the value of the peripheral’s address that you are passing. Some devices have multiple addresses, selectable by setting an address pin to one of three states (high, low or floating). The imp API takes addresses in 8-bit form; 7-bit addresses will need to be bit-shifted left one place.
Transmit Error (-3)
BTF Error (-4)
Once the peripheral has acknowledged its readiness, the imp can begin to send the data it wants to write. Data is sent byte by byte, each one written to the imp’s data register from which it is passed to a shift register and from there out onto the bus one bit at a time. Once the peripheral has clocked in a byte of data, it should acknowledged receipt. If it fails to do so while the imp is processing any byte of the data but the last, a transmit error will be issued. If the error occurs sending the last byte, a BTF error is reported instead.
A transmit error may also be reported if the bit value clocked out is not seen on the bus, ie. the imp sends a 1, ie. it leaves the SDA line high, but the SDA line is pulled low. This could indicate bus contention — there is another master on the bus — or, more likely, that the I²C circuit’s pull-up resistors are too weak to keep the lines high.
Stop Error (-5)
Once the imp has successfully written all the data it wants to send to the peripheral (or read back all the data that it requested), it signals the completion of the transaction with a stop condition: while SCL is high, SDA is released to go high too (the waveform has a rising edge). This releases the bus for other devices to make use of it. Again, the imp checks that this stop signal has been sent correctly. If it has not, then this error will be issued.
Address Clear Error (-6)
During a read operation, the imp places on the bus the I²C address of the peripheral it wants to read data from. This event should be acknowledged by the peripheral. If it is not, the imp will return this error.
Address RXNE Error (-7)
Data RXNE Error (-8)
Controller Receive Select Error (-10)
Receive Error (-11)
BTF Receive Error (-14)
All of these errors indicate a failure to read a byte from the peripheral at some point during the read operation. This is often caused by the peripheral holding the SCL line low while it retrieves the requested byte(s) — a technique called ‘clock stretching’.
Which error you get will depend on the number of bytes you have requested to be read and on the exact point where the receive error occurred.
Reselect Error (-12)
If the imp wishes to read data from a specific peripheral register, it must first write that register’s address to the peripheral as a data value. After the register address has successfully been written, the imp initiates a read operation, to pick up the value the peripheral returns from the register. The imp switches from write mode to read mode by issuing a second start signal. Once again, it checks that it can proceed. If it is unable to do so within the timeout period, this error will be issued.
Содержание
- I2C/SMBUS Fault CodesВ¶
- A “Fault” is not always an “Error”¶
- I2C and SMBus fault codesВ¶
- MicroPython Forum (Archive)
- What are the I2C error codes?
- What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- Re: What are the I2C error codes?
- I2c error code 2
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Re: error when trying to use I2C
- Understanding I2C Errors
- The Errors
- Not Enabled (-13)
- Controller Select Error (-1)
- Transmit Select Error (-2)
- Transmit Error (-3) BTF Error (-4)
- Stop Error (-5)
- Address Clear Error (-6)
- Address RXNE Error (-7) Data RXNE Error (-8) Controller Receive Select Error (-10) Receive Error (-11) BTF Receive Error (-14)
- Reselect Error (-12)
I2C/SMBUS Fault CodesВ¶
This is a summary of the most important conventions for use of fault codes in the I2C/SMBus stack.
A “Fault” is not always an “Error”¶
Not all fault reports imply errors; “page faults” should be a familiar example. Software often retries idempotent operations after transient faults. There may be fancier recovery schemes that are appropriate in some cases, such as re-initializing (and maybe resetting). After such recovery, triggered by a fault report, there is no error.
In a similar way, sometimes a “fault” code just reports one defined result for an operation … it doesn’t indicate that anything is wrong at all, just that the outcome wasn’t on the “golden path”.
In short, your I2C driver code may need to know these codes in order to respond correctly. Other code may need to rely on YOUR code reporting the right fault code, so that it can (in turn) behave correctly.
I2C and SMBus fault codesВ¶
These are returned as negative numbers from most calls, with zero or some positive number indicating a non-fault return. The specific numbers associated with these symbols differ between architectures, though most Linux systems use numbering.
Note that the descriptions here are not exhaustive. There are other codes that may be returned, and other cases where these codes should be returned. However, drivers should not return other codes for these cases (unless the hardware doesn’t provide unique fault reports).
Also, codes returned by adapter probe methods follow rules which are specific to their host bus (such as PCI, or the platform bus).
Returned by I2C adapters when they lose arbitration in master transmit mode: some other master was transmitting different data at the same time.
Also returned when trying to invoke an I2C operation in an atomic context, when some task is already using that I2C bus to execute some other operation.
Returned by SMBus logic when an invalid Packet Error Code byte is received. This code is a CRC covering all bytes in the transaction, and is sent before the terminating STOP. This fault is only reported on read transactions; the SMBus slave may have a way to report PEC mismatches on writes from the host. Note that even if PECs are in use, you should not rely on these as the only way to detect incorrect data transfers.
Returned by SMBus adapters when the bus was busy for longer than allowed. This usually indicates some device (maybe the SMBus adapter) needs some fault recovery (such as resetting), or that the reset was attempted but failed.
This rather vague error means an invalid parameter has been detected before any I/O operation was started. Use a more specific fault code when you can.
This rather vague error means something went wrong when performing an I/O operation. Use a more specific fault code when you can.
Returned by driver probe() methods. This is a bit more specific than ENXIO, implying the problem isn’t with the address, but with the device found there. Driver probes may verify the device returns correct responses, and return this as appropriate. (The driver core will warn about probe faults other than ENXIO and ENODEV.)
Returned by any component that can’t allocate memory when it needs to do so.
Returned by I2C adapters to indicate that the address phase of a transfer didn’t get an ACK. While it might just mean an I2C device was temporarily not responding, usually it means there’s nothing listening at that address.
Returned by driver probe() methods to indicate that they found no device to bind to. (ENODEV may also be used.)
Returned by an adapter when asked to perform an operation that it doesn’t, or can’t, support.
For example, this would be returned when an adapter that doesn’t support SMBus block transfers is asked to execute one. In that case, the driver making that request should have verified that functionality was supported before it made that block transfer request.
Similarly, if an I2C adapter can’t execute all legal I2C messages, it should return this when asked to perform a transaction it can’t. (These limitations can’t be seen in the adapter’s functionality mask, since the assumption is that if an adapter supports I2C it supports all of I2C.)
Returned when slave does not conform to the relevant I2C or SMBus (or chip-specific) protocol specifications. One case is when the length of an SMBus block data response (from the SMBus slave) is outside the range 1-32 bytes.
Returned when a transfer was requested using an adapter which is already suspended.
This is returned by drivers when an operation took too much time, and was aborted before it completed.
SMBus adapters may return it when an operation took more time than allowed by the SMBus specification; for example, when a slave stretches clocks too far. I2C has no such timeouts, but it’s normal for I2C adapters to impose some arbitrary limits (much longer than SMBus!) too.
Источник
MicroPython Forum (Archive)
What are the I2C error codes?
What are the I2C error codes?
Post by Gordon_Hardman » Fri Nov 14, 2014 7:04 pm
My first foray into I2C in the Pyboard. must be doing something dumb!
I am trying to talk to a DS2764 on I2C1. I am getting the following:
«Exception: HAL_I2C_xxx_Transmit failed with code 2»
Code is pretty simple, it is meaningless to the 2764, but I would have expected to see some activity. Monitoring with a scope, there is no activity on SCL or SDA. Both are pulled high, and appear to be configured correctly.
There are not many good examples out there that I could find. The forum rejects «I2C» for a search as being too common! Not much on a Google search either.
Re: What are the I2C error codes?
Post by dhylands » Fri Nov 14, 2014 7:50 pm
Why are you left shifting the address by 1 bit?
The data sheet says that the default is address is 0110100 which is 0x34 = 52
The address is 7-bits and yes the first byte sent on the bus will include the address shifted left by one bit, but that is done internally by the i2c library. Contrary to popular belief, i2c devices do not have a read address and a write address. They have a 7-bit address and 1-bit Read/write flag.
You can run i2c.scan() to see the addresses of devices on the bus.
Re: What are the I2C error codes?
Post by Gordon_Hardman » Fri Nov 14, 2014 9:24 pm
And it prints an empty list []. No activity on SCL or SDA, I even checked Y9 and Y10, just in case! With no activity on the bus, the list should be empty, I guess.
Good catch on the address, I often do it this way because the upper 7 bits are the address bits.
If I put a 4.7K to ground, the voltage drops in half, so the pins are definitely inputs.
Re: What are the I2C error codes?
Post by Gordon_Hardman » Fri Nov 14, 2014 9:57 pm
It works perfectly on the other I2C port:
i2c = I2C(2, I2C.MASTER) # create and init as a master
No idea why, but I am not using that for anything, so I have a solution, so long as I only need the one I2C.
Re: What are the I2C error codes?
Post by dhylands » Fri Nov 14, 2014 10:12 pm
I assume you’re moving pins when you change from i2c1 to i2c2? You probably are, I’m just making sure.
And you’ve got ground hooked up between the pyboard and your device?
The on board accelerometer is also on bus 1, at address 76.
then it will take it out of reset and it should show up on i2c1
Re: What are the I2C error codes?
Post by Gordon_Hardman » Fri Nov 14, 2014 11:14 pm
This is what I get:
It just froze up on the second I2C port as well, but when I cycled the USB power, it came back- this did not work for I2C1.
Re: What are the I2C error codes?
Post by Gordon_Hardman » Fri Nov 14, 2014 11:16 pm
Re: What are the I2C error codes?
Post by dhylands » Fri Nov 14, 2014 11:24 pm
Re: What are the I2C error codes?
Post by Gordon_Hardman » Fri Nov 14, 2014 11:36 pm
Re: What are the I2C error codes?
Post by dhylands » Sat Nov 15, 2014 1:13 am
I don’t believe that Control-D does anything to the i2c (as far as uninitialization goes).
So if the i2c gets «hung» in a way that just reinitializing doesn’t clear things up then Control-D also won’t do anything. A power cycle will completely reset all of the hardware.
Источник
I2c error code 2
Post by mzimmers » Mon Nov 05, 2018 7:48 pm
We’re experimenting with a fuel gauge that we’d connect via I2C. I’m trying to follow the docs, but my initial attempts are returning an error. Here’s the code:
Re: error when trying to use I2C
Post by mikemoy » Mon Nov 05, 2018 9:28 pm
I am using these pins for I2C.
#define I2C_EXAMPLE_MASTER_SCL_IO GPIO_NUM_5 // gpio number for I2C master clock
#define I2C_EXAMPLE_MASTER_SDA_IO GPIO_NUM_4 // gpio number for I2C master data
I also noticed that you init you have GPIO_PULLUP_DISABLE. Do you have external resistor pull-ups on both lines?
Here is my init.
Re: error when trying to use I2C
Post by mzimmers » Mon Nov 05, 2018 9:53 pm
Hi Mike — thanks for the code snippet. When I read it, I realized I’d missed a step in the docs, namely, the call to i2c_param_config(). I no longer get the error regarding a bad GPIO number. I still get an error 0x107, though, on my call to i2c_master_cmd_begin().
Not sure whether I should be enabling pullups or not; I’ll check with my HW guy and report back. But I tried it both ways, with identical results.
Re: error when trying to use I2C
Post by mikemoy » Mon Nov 05, 2018 10:06 pm
Just for giggles try increasing your ticks_to_wait, in i2c_master_cmd_begin. I think yous is way to short being a 1.
Having it larger does not hurt. For instance, even if you set it for 1 minute (a total exaggeration) it would still exit as soon as it got the response back.
Re: error when trying to use I2C
Post by mzimmers » Mon Nov 05, 2018 10:43 pm
I should point out that the problem may legitimately be in the hardware, as our board is still in design. I just wanted to try to make sure my code was clean first.
BTW: this is what my final operation will look like (when I get it working):
Re: error when trying to use I2C
Post by mikemoy » Mon Nov 05, 2018 10:57 pm
Re: error when trying to use I2C
Post by mzimmers » Mon Nov 05, 2018 11:48 pm
Re: error when trying to use I2C
Post by mikemoy » Tue Nov 06, 2018 12:42 am
The best advise i can give when starting or using I2C on a new platform is use a I2C device that you can write to without having to read from. Reading is more difficult to debug on a new platform because you wont know if it’s your writes or reads that are screwed up.
Most guys don’t have a logic analyzer so they go at it in the blind and hope for the best
I have my own boards that I built to test I2C on new platforms. But looking on the net for something I think this would be a good one to start with.
https://www.mikroe.com/led-driver-3-click
Re: error when trying to use I2C
Post by fly135 » Tue Nov 06, 2018 3:36 am
I recently bought one of these. It’s an I2C device.
There is C code than can be downloaded, and I had no problem getting it to work. Or you can just read the chip id address to verify that it’s responding to your I2C commands.
Re: error when trying to use I2C
Post by mzimmers » Tue Nov 06, 2018 5:22 pm
Well, my HW guys have checked out the board and say it looks OK, so I’m inclined to think I’m doing something wrong.
Here’s a code snippet of my attempt to implement the protocol described in the drawing in my above post.
Does anything jump out at you as wrong here?
Источник
Understanding I2C Errors
How To Debug I²C Read And Write Problems
The imp API provides users of I²C peripheral devices (‘peripherals’) with debugging information following failed attempts to read or write data to any I²C peripheral connected to an imp. This information comes in the form of an integer constant returned by either i2c.write() or i2c.readerror(), depending on the type of operation being performed. If either method returns any value other than 0, an error has occurred. The return value indicates the nature of the error, and these errors are listed in the i2c.readerror() documentation. Should an error occur, the I²C transaction is abandoned.
This document describes these errors in more detail. They are presented in the order in which they are likely to be encountered, with those relating to write operations appearing first, followed by those issued during read operations.
The imp API makes sending and receiving data via I²C very straightforward, but ‘under the hood’ the process as is complex. As such, a single imp API I²C access may result in any one of a series of errors, depending on which stage of the process the underlying impOS™ I²C code has reached.
The I²C Protocol
Developers working with imp005-based devices should note that the following applies primarily to other imp devices. The imp005’s Broadcom SoC will report only two I²C error values: -13 and -9. The former is as described below, but the latter covers almost all of the remaining I²C errors: unfortunately, some error conditions aren’t reported at all (success is falsely indicated). We hope to improve imp005 I²C error reporting in a future version of impOS.
The Errors
Not Enabled (-13)
This is a straightforward error that is issued when you have attempted to access an imp I²C bus that has not yet been configured. Check your code and, if necessary, call i2c.configure() with a supported speed constant passed as a parameter.
Controller Select Error (-1)
The imp signals its intention to begin an I²C transaction by establishing the standard I²C start condition: it attempts to pull the SDA line low (so the waveform has a falling edge) while the SCL line remains high. If the imp is unable to pull SDA low, this error will be issued.
This error may arise if another I²C master is operating on the same bus and has taken control of it. If the imp is the only master on the bus, this error may result from poorly chosen pull-up resistors. I²C ports are open drain so can only pull the SDA and SCL lines low; pull-up resistors are required to drive the lines high when they are released by bus devices.
Transmit Select Error (-2)
After the imp has signalled start and is ready to write data to it, it sends the 7-bit address of the I²C peripheral it wants to communicate with, followed by a single bit indicating whether the transaction is a write (the imp pulls SDA low) or a read (the imp leaves SDA high). These eight bits should be acknowledged by a single-bit ACK signal from the peripheral at the transmitted address; it pulls SDA low. If the acknowledgement doesn’t occur during the ninth clock pulse, then the imp will issue this error.
If this error is encountered, check the value of the peripheral’s address that you are passing. Some devices have multiple addresses, selectable by setting an address pin to one of three states (high, low or floating). The imp API takes addresses in 8-bit form; 7-bit addresses will need to be bit-shifted left one place.
Transmit Error (-3)
BTF Error (-4)
Once the peripheral has acknowledged its readiness, the imp can begin to send the data it wants to write. Data is sent byte by byte, each one written to the imp’s data register from which it is passed to a shift register and from there out onto the bus one bit at a time. Once the peripheral has clocked in a byte of data, it should acknowledged receipt. If it fails to do so while the imp is processing any byte of the data but the last, a transmit error will be issued. If the error occurs sending the last byte, a BTF error is reported instead.
A transmit error may also be reported if the bit value clocked out is not seen on the bus, ie. the imp sends a 1, ie. it leaves the SDA line high, but the SDA line is pulled low. This could indicate bus contention — there is another master on the bus — or, more likely, that the I²C circuit’s pull-up resistors are too weak to keep the lines high.
Stop Error (-5)
Once the imp has successfully written all the data it wants to send to the peripheral (or read back all the data that it requested), it signals the completion of the transaction with a stop condition: while SCL is high, SDA is released to go high too (the waveform has a rising edge). This releases the bus for other devices to make use of it. Again, the imp checks that this stop signal has been sent correctly. If it has not, then this error will be issued.
Address Clear Error (-6)
During a read operation, the imp places on the bus the I²C address of the peripheral it wants to read data from. This event should be acknowledged by the peripheral. If it is not, the imp will return this error.
Address RXNE Error (-7)
Data RXNE Error (-8)
Controller Receive Select Error (-10)
Receive Error (-11)
BTF Receive Error (-14)
All of these errors indicate a failure to read a byte from the peripheral at some point during the read operation. This is often caused by the peripheral holding the SCL line low while it retrieves the requested byte(s) — a technique called ‘clock stretching’.
Which error you get will depend on the number of bytes you have requested to be read and on the exact point where the receive error occurred.
Reselect Error (-12)
If the imp wishes to read data from a specific peripheral register, it must first write that register’s address to the peripheral as a data value. After the register address has successfully been written, the imp initiates a read operation, to pick up the value the peripheral returns from the register. The imp switches from write mode to read mode by issuing a second start signal. Once again, it checks that it can proceed. If it is unable to do so within the timeout period, this error will be issued.
Источник
NAME
I2C module for Linux userspace i2c-dev devices.
SYNOPSIS
local periphery = require('periphery') local I2C = periphery.I2C -- Constructor i2c = I2C(device <path string>) i2c = I2C{device=<path string>} -- Methods i2c:transfer(address <number>, messages <table>) i2c:close() -- Properties i2c.fd immutable <number> -- Constants I2C.I2C_M_TEN I2C.I2C_M_RD I2C.I2C_M_STOP I2C.I2C_M_NOSTART I2C.I2C_M_REV_DIR_ADDR I2C.I2C_M_IGNORE_NAK I2C.I2C_M_NO_RD_ACK I2C.I2C_M_RECV_LEN
CONSTANTS
- I2C Message Flags
I2C.I2C_M_TENI2C.I2C_M_RDI2C.I2C_M_STOPI2C.I2C_M_NOSTARTI2C.I2C_M_REV_DIR_ADDRI2C.I2C_M_IGNORE_NAKI2C.I2C_M_NO_RD_ACKI2C.I2C_M_RECV_LEN
DESCRIPTION
I2C(device <path string>) --> <I2C object> I2C{device=<path string>} --> <I2C object>
Instantiate an I2C object and open the i2c-dev device at the specified path.
Example:
i2c = I2C("/dev/i2c-1") i2c = I2C{device="/dev/i2c-1"}
Returns a new I2C object on success. Raises an I2C error on failure.
i2c:transfer(address <number>, messages <table>)
Transfer messages to the specified I2C address. Modifies the messages table with the results of any read transactions.
The messages table is an array of message tables. Each message table is an array of bytes, as well a flags field containing any bitwise-ORd message flags (see the constants section above). The most common message flag is the I2C.I2C_M_RD flag, which specifies a read transaction. The read length is inferred from the number of placeholder bytes in the message table.
Example:
local msgs = { { 0xaa, 0xbb }, { 0x00, 0x00, 0x00, flags = I2C.I2C_M_RD } } i2c:transfer(0x50, msgs)
The msgs messages table above specifies one write transaction with two bytes 0xaa, 0xbb, and one read transaction with placeholders for three bytes. After the transfer completes successfully, the read message contents (0x00, 0x00, 0x00) will be replaced with the data read from the I2C bus in that read transaction.
Raises an I2C error on failure.
Close the I2C device.
Raises an I2C error on failure.
Property i2c.fd immutable <number>
Get the file descriptor of the underlying i2c-dev device.
Raises an I2C error on assignment.
ERRORS
The periphery I2C methods and properties may raise a Lua error on failure that can be propagated to the user or caught with Lua’s pcall(). The error object raised is a table with code, c_errno, message properties, which contain the error code string, underlying C error number, and a descriptive message string of the error, respectively. The error object also provides the necessary metamethod for it to be formatted as a string if it is propagated to the user by the interpreter.
--- Example of error propagated to user > periphery = require('periphery') > i2c = periphery.I2C("/dev/i2c-1") Opening I2C device "/dev/i2c-1": No such file or directory [errno 2] > --- Example of error caught with pcall() > status, err = pcall(function () i2c = periphery.I2C("/dev/i2c-1") end) > =status false > dump(err) { c_errno = 2, code = "I2C_ERROR_OPEN", message = "Opening I2C device "/dev/i2c-1": No such file or directory [errno 2]" } >
| Error Code | Description |
|---|---|
"I2C_ERROR_ARG" |
Invalid arguments |
"I2C_ERROR_OPEN" |
Opening I2C device |
"I2C_ERROR_QUERY" |
Querying I2C device attribtues |
"I2C_ERROR_NOT_SUPPORTED" |
I2C not supported on this device |
"I2C_ERROR_TRANSFER" |
I2C transfer |
"I2C_ERROR_CLOSE" |
Closing I2C device |
"I2C_ERROR_ALLOC" |
Allocating memory |
EXAMPLE
local I2C = require('periphery').I2C -- Open i2c-0 controller local i2c = I2C("/dev/i2c-0") --- Read byte at address 0x100 of EEPROM at 0x50 local msgs = { {0x01, 0x00}, {0x00, flags = I2C.I2C_M_RD} } i2c:transfer(0x50, msgs) print(string.format("0x100: 0x%02x", msgs[2][1])) i2c:close()
-
Gordon_Hardman
- Posts: 68
- Joined: Sat May 03, 2014 11:31 pm
What are the I2C error codes?
My first foray into I2C in the Pyboard… must be doing something dumb!
I am trying to talk to a DS2764 on I2C1. I am getting the following:
«Exception: HAL_I2C_xxx_Transmit failed with code 2»
Code is pretty simple, it is meaningless to the 2764, but I would have expected to see some activity. Monitoring with a scope, there is no activity on SCL or SDA. Both are pulled high, and appear to be configured correctly.
There are not many good examples out there that I could find. The forum rejects «I2C» for a search as being too common! Not much on a Google search either.
Code: Select all
# main.py -- put your code here!
import pyb
from pyb import I2C
i2c = I2C(1, I2C.MASTER) # create and init as a master
i2c.init(I2C.MASTER, baudrate=20000) # init as a master
addr = 52 << 1
while True:
i2c.send('123', addr)
pyb.delay(100)
-
dhylands
- Posts: 3821
- Joined: Mon Jan 06, 2014 6:08 pm
- Location: Peachland, BC, Canada
- Contact:
Re: What are the I2C error codes?
Post
by dhylands » Fri Nov 14, 2014 7:50 pm
Why are you left shifting the address by 1 bit?
The data sheet says that the default is address is 0110100 which is 0x34 = 52
The address is 7-bits and yes the first byte sent on the bus will include the address shifted left by one bit, but that is done internally by the i2c library. Contrary to popular belief, i2c devices do not have a read address and a write address. They have a 7-bit address and 1-bit Read/write flag.
You can run i2c.scan() to see the addresses of devices on the bus.
-
Gordon_Hardman
- Posts: 68
- Joined: Sat May 03, 2014 11:31 pm
Re: What are the I2C error codes?
Post
by Gordon_Hardman » Fri Nov 14, 2014 9:24 pm
Okay- I changed the code to:
Code: Select all
import pyb
from pyb import I2C
i2c = I2C(1, I2C.MASTER) # create and init as a master
i2c.init(I2C.MASTER, baudrate=20000) # init as a master
addr = 52
while True:
addr_list=[]
addr_list = i2c.scan()
print(addr_list)
pyb.delay(2000)
And it prints an empty list []. No activity on SCL or SDA, I even checked Y9 and Y10, just in case! With no activity on the bus, the list should be empty, I guess.
Good catch on the address, I often do it this way because the upper 7 bits are the address bits.
If I put a 4.7K to ground, the voltage drops in half, so the pins are definitely inputs.
-
Gordon_Hardman
- Posts: 68
- Joined: Sat May 03, 2014 11:31 pm
Re: What are the I2C error codes?
Post
by Gordon_Hardman » Fri Nov 14, 2014 9:57 pm
It works perfectly on the other I2C port:
i2c = I2C(2, I2C.MASTER) # create and init as a master
No idea why, but I am not using that for anything, so I have a solution, so long as I only need the one I2C.
-
dhylands
- Posts: 3821
- Joined: Mon Jan 06, 2014 6:08 pm
- Location: Peachland, BC, Canada
- Contact:
Re: What are the I2C error codes?
Post
by dhylands » Fri Nov 14, 2014 10:12 pm
I assume you’re moving pins when you change from i2c1 to i2c2? You probably are, I’m just making sure.
And you’ve got ground hooked up between the pyboard and your device?
The on board accelerometer is also on bus 1, at address 76.
If you do:
accel = pyb.Accel()
then it will take it out of reset and it should show up on i2c1
Code: Select all
>>> i2c = pyb.I2C(1, pyb.I2C.MASTER)
>>> i2c.scan()
[]
>>> pyb.Accel()
<Accel>
>>> i2c.scan()
[76]
-
Gordon_Hardman
- Posts: 68
- Joined: Sat May 03, 2014 11:31 pm
Re: What are the I2C error codes?
Post
by Gordon_Hardman » Fri Nov 14, 2014 11:14 pm
This is what I get:
i2c = pyb.I2C(1, pyb.I2C.MASTER)
>>> i2c.scan()
[]
>>> pyb.Accel()
<Accel>
>>> i2c.scan()
[]
>>>
It just froze up on the second I2C port as well, but when I cycled the USB power, it came back- this did not work for I2C1.
-
dhylands
- Posts: 3821
- Joined: Mon Jan 06, 2014 6:08 pm
- Location: Peachland, BC, Canada
- Contact:
Re: What are the I2C error codes?
Post
by dhylands » Fri Nov 14, 2014 11:24 pm
If you try the scan/accel with no extra HW hooked up does it still not show anything? If the scan on i2c1 comes up empty when the acceleration is enabled and no external HW hooked up then I think you’ve got a bad board.
-
Gordon_Hardman
- Posts: 68
- Joined: Sat May 03, 2014 11:31 pm
Re: What are the I2C error codes?
Post
by Gordon_Hardman » Fri Nov 14, 2014 11:36 pm
Well, I have to conclude there is either something wrong with the initialization, or the way I am doing it. It just started working on I2C1. One thing that is definitely true, it seems that the ctrl-C ctrl-D way of rebooting is different from cycling power! I cycled power numerous times, with no joy on I2C1, and then suddenly it started working. And on I2C2, it comes to life every time I cycle power, even after it has «hung».
-
dhylands
- Posts: 3821
- Joined: Mon Jan 06, 2014 6:08 pm
- Location: Peachland, BC, Canada
- Contact:
Re: What are the I2C error codes?
Post
by dhylands » Sat Nov 15, 2014 1:13 am
I don’t believe that Control-D does anything to the i2c (as far as uninitialization goes).
So if the i2c gets «hung» in a way that just reinitializing doesn’t clear things up then Control-D also won’t do anything. A power cycle will completely reset all of the hardware.
A couple other options worth trying:
1 — The reset button. I expect that this would be very similar to a power cycle, but external circuitry won’t be reset.
2 — There is a pyb.hard_reset() which is about as close as we can get to pressing the reset button from software without tying up a GPIO line.
I’m new to Arduino and Stack Exchange. I’ve been building a fitness tracker and am receiving a certain error in the serial monitor. The accelerometer seems the work, but it seems to go to sleep after a while. I’m using an Arduino Uno (for the time being), a Bluetooth HC-05 module and an MPU6050 accelerometer. I connect it to an app, Retroband to monitor the steps taken and calories burnt.
I’ve wired it like this:
Accelerometer:
SDA-A4
SCL-A5
VCC-3.3v
GND-GND
Bluetooth:
VCC-3.3v
GND-GND
TX-2
RX-3
Here is the code I’m using:
#include <math.h>
#include <Wire.h>
#include <SoftwareSerial.h>
/* Bluetooth */
SoftwareSerial BTSerial(2, 3); //Connect HC-06. Use your (TX, RX) settings
/* time */
#define SENDING_INTERVAL 1000
#define SENSOR_READ_INTERVAL 50
unsigned long prevSensoredTime = 0;
unsigned long curSensoredTime = 0;
/* Data buffer */
#define ACCEL_BUFFER_COUNT 125
byte aAccelBuffer[ACCEL_BUFFER_COUNT];
int iAccelIndex = 2;
/* MPU-6050 sensor */
#define MPU6050_ACCEL_XOUT_H 0x3B // R
#define MPU6050_PWR_MGMT_1 0x6B // R/W
#define MPU6050_PWR_MGMT_2 0x6C // R/W
#define MPU6050_WHO_AM_I 0x75 // R
#define MPU6050_I2C_ADDRESS 0x68
typedef union accel_t_gyro_union {
struct {
uint8_t x_accel_h;
uint8_t x_accel_l;
uint8_t y_accel_h;
uint8_t y_accel_l;
uint8_t z_accel_h;
uint8_t z_accel_l;
uint8_t t_h;
uint8_t t_l;
uint8_t x_gyro_h;
uint8_t x_gyro_l;
uint8_t y_gyro_h;
uint8_t y_gyro_l;
uint8_t z_gyro_h;
uint8_t z_gyro_l;
} reg;
struct {
int x_accel;
int y_accel;
int z_accel;
int temperature;
int x_gyro;
int y_gyro;
int z_gyro;
} value;
};
void setup() {
int error;
uint8_t c;
Serial.begin(9600);
Wire.begin();
BTSerial.begin(9600); // set the data rate for the BT port
// default at power-up:
// Gyro at 250 degrees second
// Acceleration at 2g
// Clock source at internal 8MHz
// The device is in sleep mode.
//
error = MPU6050_read (MPU6050_WHO_AM_I, &c, 1);
Serial.print(F("WHO_AM_I : "));
Serial.print(c,HEX);
Serial.print(F(", error = "));
Serial.println(error,DEC);
// According to the datasheet, the 'sleep' bit
// should read a '1'. But I read a '0'.
// That bit has to be cleared, since the sensor
// is in sleep mode at power-up. Even if the
// bit reads '0'.
error = MPU6050_read (MPU6050_PWR_MGMT_2, &c, 1);
Serial.print(F("PWR_MGMT_2 : "));
Serial.print(c,HEX);
Serial.print(F(", error = "));
Serial.println(error,DEC);
// Clear the 'sleep' bit to start the sensor.
MPU6050_write_reg (MPU6050_PWR_MGMT_1, 0);
initBuffer();
}
void loop() {
curSensoredTime = millis();
// Read from sensor
if(curSensoredTime - prevSensoredTime > SENSOR_READ_INTERVAL) {
readFromSensor(); // Read from sensor
prevSensoredTime = curSensoredTime;
// Send buffer data to remote
if(iAccelIndex >= ACCEL_BUFFER_COUNT - 3) {
sendToRemote();
initBuffer();
Serial.println("------------- Send 20 accel data to remote");
}
}
}
/**************************************************
* BT Transaction
**************************************************/
void sendToRemote() {
// Write gabage bytes
BTSerial.write( "accel" );
// Write accel data
BTSerial.write( (char*)aAccelBuffer );
// Flush buffer
//BTSerial.flush();
}
/**************************************************
* Read data from sensor and save it
**************************************************/
void readFromSensor() {
int error;
double dT;
accel_t_gyro_union accel_t_gyro;
error = MPU6050_read (MPU6050_ACCEL_XOUT_H, (uint8_t *) &accel_t_gyro, sizeof(accel_t_gyro));
if(error != 0) {
Serial.print(F("Read accel, temp and gyro, error = "));
Serial.println(error,DEC);
}
// Swap all high and low bytes.
// After this, the registers values are swapped,
// so the structure name like x_accel_l does no
// longer contain the lower byte.
uint8_t swap;
#define SWAP(x,y) swap = x; x = y; y = swap
SWAP (accel_t_gyro.reg.x_accel_h, accel_t_gyro.reg.x_accel_l);
SWAP (accel_t_gyro.reg.y_accel_h, accel_t_gyro.reg.y_accel_l);
SWAP (accel_t_gyro.reg.z_accel_h, accel_t_gyro.reg.z_accel_l);
SWAP (accel_t_gyro.reg.t_h, accel_t_gyro.reg.t_l);
SWAP (accel_t_gyro.reg.x_gyro_h, accel_t_gyro.reg.x_gyro_l);
SWAP (accel_t_gyro.reg.y_gyro_h, accel_t_gyro.reg.y_gyro_l);
SWAP (accel_t_gyro.reg.z_gyro_h, accel_t_gyro.reg.z_gyro_l);
// Print the raw acceleration values
Serial.print(F("accel x,y,z: "));
Serial.print(accel_t_gyro.value.x_accel, DEC);
Serial.print(F(", "));
Serial.print(accel_t_gyro.value.y_accel, DEC);
Serial.print(F(", "));
Serial.print(accel_t_gyro.value.z_accel, DEC);
Serial.print(F(", at "));
Serial.print(iAccelIndex);
Serial.println(F(""));
if(iAccelIndex < ACCEL_BUFFER_COUNT && iAccelIndex > 1) {
int tempX = accel_t_gyro.value.x_accel;
int tempY = accel_t_gyro.value.y_accel;
int tempZ = accel_t_gyro.value.z_accel;
/*
// Check min, max value
if(tempX > 16380) tempX = 16380;
if(tempY > 16380) tempY = 16380;
if(tempZ > 16380) tempZ = 16380;
if(tempX < -16380) tempX = -16380;
if(tempY < -16380) tempY = -16380;
if(tempZ < -16380) tempZ = -16380;
// We dont use negative value
tempX += 16380;
tempY += 16380;
tempZ += 16380;
*/
char temp = (char)(tempX >> 8);
if(temp == 0x00)
temp = 0x7f;
aAccelBuffer[iAccelIndex] = temp;
iAccelIndex++;
temp = (char)(tempX);
if(temp == 0x00)
temp = 0x01;
aAccelBuffer[iAccelIndex] = temp;
iAccelIndex++;
temp = (char)(tempY >> 8);
if(temp == 0x00)
temp = 0x7f;
aAccelBuffer[iAccelIndex] = temp;
iAccelIndex++;
temp = (char)(tempY);
if(temp == 0x00)
temp = 0x01;
aAccelBuffer[iAccelIndex] = temp;
iAccelIndex++;
temp = (char)(tempZ >> 8);
if(temp == 0x00)
temp = 0x7f;
aAccelBuffer[iAccelIndex] = temp;
iAccelIndex++;
temp = (char)(tempZ);
if(temp == 0x00)
temp = 0x01;
aAccelBuffer[iAccelIndex] = temp;
iAccelIndex++;
}
// The temperature sensor is -40 to +85 degrees Celsius.
// It is a signed integer.
// According to the datasheet:
// 340 per degrees Celsius, -512 at 35 degrees.
// At 0 degrees: -512 - (340 * 35) = -12412
// Serial.print(F("temperature: "));
// dT = ( (double) accel_t_gyro.value.temperature + 12412.0) / 340.0;
// Serial.print(dT, 3);
// Serial.print(F(" degrees Celsius"));
// Serial.println(F(""));
// Print the raw gyro values.
// Serial.print(F("gyro x,y,z : "));
// Serial.print(accel_t_gyro.value.x_gyro, DEC);
// Serial.print(F(", "));
// Serial.print(accel_t_gyro.value.y_gyro, DEC);
// Serial.print(F(", "));
// Serial.print(accel_t_gyro.value.z_gyro, DEC);
// Serial.println(F(""));
}
/**************************************************
* MPU-6050 Sensor read/write
**************************************************/
int MPU6050_read(int start, uint8_t *buffer, int size)
{
int i, n, error;
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
n = Wire.write(start);
if (n != 1)
return (-10);
n = Wire.endTransmission(false); // hold the I2C-bus
if (n != 0)
return (n);
// Third parameter is true: relase I2C-bus after data is read.
Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true);
i = 0;
while(Wire.available() && i<size)
{
buffer[i++]=Wire.read();
}
if ( i != size)
return (-11);
return (0); // return : no error
}
int MPU6050_write(int start, const uint8_t *pData, int size)
{
int n, error;
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
n = Wire.write(start); // write the start address
if (n != 1)
return (-20);
n = Wire.write(pData, size); // write data bytes
if (n != size)
return (-21);
error = Wire.endTransmission(true); // release the I2C-bus
if (error != 0)
return (error);
return (0); // return : no error
}
int MPU6050_write_reg(int reg, uint8_t data)
{
int error;
error = MPU6050_write(reg, &data, 1);
return (error);
}
/**************************************************
* Utilities
**************************************************/
void initBuffer() {
iAccelIndex = 2;
for(int i=iAccelIndex; i<ACCEL_BUFFER_COUNT; i++) {
aAccelBuffer[i] = 0x00;
}
aAccelBuffer[0] = 0xfe;
aAccelBuffer[1] = 0xfd;
aAccelBuffer[122] = 0xfd;
aAccelBuffer[123] = 0xfe;
aAccelBuffer[124] = 0x00;
}
This is the error I got in serial monitor:
WHO_AM_I : 0, error = 2
PWR_MGMT_2 : 0, error = 2
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, -3840, at 2
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 8
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 14
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 20
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 26
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 32
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 38
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 44
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 50
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 56
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 62
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 68
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 74
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 80
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 86
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 92
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 98
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 104
Read accel, temp and gyro, error = 2
accel x,y,z: 2969, 409, 123, at 110
Read accel, temp and gyro, error = 2
accel x,y,z: -26357, -26367, 31488, at 116
I’m not too good at Arduino, and any help will be immensely appreciated.
Ошибка по шине i2c
Поиск неисправностей в системной шине I 2 C
Напряжение питания как к линии SDA , так и к линии SCL подводится через нагрузочные резисторы. Значение этого напряжения обычно составляет от 4,5 В до 5,5 В и должно точно соответствовать приведенному в сервисной документации уровню. Поиск неисправностей, поэтому следует начинать с измерения питающего шину напряжения. К прекращению обмена информацией в шине могут привести также колебания питающего напряжения питания, особенно в тех случаях, когда появляется какая-нибудь нерегулярная неисправность. Пульсации могут составлять всего лишь несколько милливольт, поэтому в сомнительных случаях проверку питающего системную шину напряжения надо проводить с помощью осоцилографа.
Обычно на входах блоков, подключаемых к системной шине, или перед микросхемами в этих блоках располагают еще и развязывающие резисторы. С помощью измерения напряжения на этих резисторах можно установить наличие короткого замыкания в соответствующем блоке. Если такая неисправность присутствует, то измеренное напряжение на выводе резистора, со стороны подключенного блока или микросхемы, имеет существенно более низкое значение, чем на другом выводе резистора. Если же в проверяемых узлах короткое замыкание отсутствует, то падение амплитуды сигнала на соответствующих развязывающих резисторах незначительное из-за достаточно низкого их номинала (от 100 Ом до 1 кОм). Поиск неисправностей усложняется, когда отдельные узлы и схемы подключены к системной шине без развязывающих резисторов, так как в этом случае любой неисправный узел может полностью заблокировать обмен информацией по системной шине. В этом случае придется последовательно отсоединять подключенные к шине отдельные узлы и схемы.
Уже измерение напряжения до и после нагрузочных резисторов может дать указание на вероятную неисправность в одном из устройств, подключенных к системной шине, если падение напряжения на этих резисторах слишком велико.
Необходима также проверка наличия сигналов в линиях SCL и SDA шины. Отсутствие сигнала синхронизации в линии SCL указывает на необходимость проверки работоспособности центрального управляющего устройства, а также внешнего кварцевого резонатора тактового генератора ЦУУ. Неисправности частотозадающих элементов тактового генератора могут быть причиной отличия тактовой частоты от номинальной, что также может привести к нарушению обмена информацией в шине.
Тактовый сигнал может представлять собой синусоидальный сигнал, либо сигнал прямоугольной или трапециевидной формы.
Наличие импульсных сигналов номинальной амплитуды в линии SDA -шины само по себе не дает достоверной информации о правильности обмена данными между узлами ВМ, подключенными к системной шине, но при их наличии можно условно считать, что обмен данными в системе происходит правильно.
Источник
Обработка ошибок и перезапуск модуля I2C
Сегодня в комментариях меня попросили рассмотреть работу I2C более подробно, обратить внимание на нетривиальные случаи: например, что будет в случае возникновения ошибок на линии, как такие ошибки обрабатывать? Дело в том, что при появлении таких ошибок модуль I2C часто «зависает», и не реагирует на дальнейшие обращения — нужно ловить такую ситуацию и перезапускать модуль.
Стандартная процедура общения с I2C-модулем предусматривает отправку байт и проверку флага — передано или нет. Флаг проверяется в цикле while, и именно этот цикл подвержен зависаниям — если флаг не устанавливается из-за ошибки, из этого цикла мы уже не выйдем никогда.
Я предлагаю простой способ выхода из этой ситуации — вместо простого while нужно сделать «while с условием». К примеру, если цикл опроса флага безрезультатно прокрутился более 200 раз — наверное ситуация уже не изменится, т.к. возникла ошибка. Значит нужно перезагружать модуль I2C.
Сделать это просто: добавим переменную «таймаут» = 200, и в цикле опроса флага будем её декрементировать. Как только она дошла до нуля — сбрасываем текущую передачу и перезапускаем модуль. Очень важно именно отменить передачу, потому что иначе после перезапуска модуля она продолжится с того места, где застряла в прошлый раз — ну и ни к чему хорошему это не приведёт.
Код примера, демонстрирующего эту «безопасную передачу», таков:
И в заключение пару слов, почему я сделал именно так, ведь более логичным было бы добавление прерываний на ошибки и обработка этих прерываний. Я просто хотел как можно меньше менять исходный код, сделать это проще всего оказалось именно расширив стандартный while до такого «while с таймаутом». Более того, так можно обойтись без глобальных переменных, а это всегда очень хорошо.
Введение обработчиков прерываний ошибок раздуло бы код и спрятало бы основную логику. Мне это неудобно. Кстати, подобная проблема встречалась у меня при работе микроконтроллера LM3S с RFID-ридером, я думаю такой подход решил бы её.
Источник
Многобукфф
Vladislav’s personal blog site
Нестабильная работа с I2C под STM32
Волею судеб мне пришлось разрабатывать прошивку для одного устройства на основе микроконтроллера STM32F103. Функций у устройства много, в том числе и общение с EEPROM подключенным посредством протокола I 2 C. Кто не знает, микроконтроллеры STM32 во многих своих версиях поддерживают работу по данному протоколу на аппаратном уровне. Это значит, что у микросхемы микроконтроллера присутствуют специальные выводы, которые можно использовать в том числе и для работы по протоколу I 2 C, а все издержки по этому протоколу выполняются «железом» микроконтроллера.
Вообще, I 2 C — штука популярная. Реализуется не так сложно, для его работы требуется всего два сигнальных провода. По одному подаются тактовые импульсы, по второму происходит передача данных, привязанная к тактам первого провода. К шире или выводам I 2 C можно подключить несколько устройств, они не будут мешать друг-другу, т.к. при обращении к конкретному устройству указывается его уникальный адрес.
Шина I 2 C не высокоскоростная и предназначена в первую очередь для обмена данными с различными датчиками, модулями и внешними системами. Через шину прокачать много информации не выйдет, но этого и не требуется. Главное, что она проста, дешева и универсальна. Ну много ли данных передает в секунду датчик температуры или давления? Сущие байты. Этого вполне достаточно.
Как правило в шине I 2 C применяется система с одним ведущим устройством и подключаемыми к нему ведомыми. В качестве ведущего устройства, разумеется, используется микроконтроллер. И он опрашивает подключенные устройства, в надежде получить с них данные. Каким же образом передаются данные по-фактически одному проводу? Конечно, передача данных по одному проводу в I 2 C не является полнодуплексной. Нет возможности в стандарте по одному проводу передавать и принимать данные одновременно. Поэтому, команды на передачу дает ведущее устройство, а все остальные слушают и отвечают, когда это им позволяется.
Простота протокола I 2 C иногда оборачивается и обратной стороной. Отлаживать проблемы, возникающие в коммуникации с внешними устройствами зачастую очень не просто. Ведь в цифровом мире либо устройство работает, либо нет. А еще больше усложняет проблему случай, когда вроде бы работает, а потом, по какой-то причине не совсем работает. Вот именно такая петрушка и произошла в моем случае.
Для реализации микропрограммы был выбран фреймворк STM32Arduino, так как требовалось использовать некоторые библиотеки, которые легче взять готовые, нежели разрабатывать их заново. К чипу же STM32 подключена обычная микросхема EEPROM на несколько килобит. EEPROM используется для частых записей, для чего не предназначена Flash-память на чипе STM32. Все аппаратные подключения проведены в строгом соответствии с документацией как производителя микроконтроллера, так и микросхемы EEPROM. И именно проверка того, как сделаны аппаратные подключения, надежно ли питание, есть ли все необходимые подтяжки и прочее, должна происходить в самую первую очередь, если возникла проблема. Иначе можно потратить годы на то, чтобы найти программную причину ошибки, особенно там, где ее нет.
В моем случае проблема заключалась в выдаче недостоверных результатов с EEPROM и невозможность записи. Точнее запись проходила, но на конечный осмысленный результат они никак не влияла. Причем неполадка появлялась только после аппаратного перезапуска устройства и примерно один раз из десяти. Присутствие какой-либо адекватной реакции от всех программах слоев, на которые опирается STM32Duino ожидать не стоит. I 2 C протокол простой и он либо работает, либо нет. И он работал, выдавал данные, причем даже если данные с EEPROM приходили откровенно левые, то никакие ошибки обращения с библиотекой Wire не возникало. Пришлось начать копать интернет в поисках похожих ошибок и методов их решения.
Как оказалось, проблема при работе с I 2 C на чипах STM32, особенно семейства F103, возникает чуть ли не у каждого второго пользователя чипов. Причем независимо от того, на чем он пишет свой код: HAL, Arduino, Mbed или еще чего. Проблем возникает много, у кого-то ничего не работает сразу, что несколько легче, так как искать ошибку проще, а у других все работает из коробки, но не постоянно. Основные проблемы, на которые натыкаются пользователи кроются в некоторых, назовем их так, особенностях структуры чипов STM32F10x, да ошибках, которые присутствуют в HAL.
Приведу основные причины возникновения неполадок с I 2 C, полученные после изучения «всего интернета»:
- Ненадежное аппаратное подключение, ненадежное неверное питание, несоблюдение рекомендаций по подключению.
- Блокировка шины на стороне микроконтроллера со статусом Busy.
- Перепутанные выходы, перепутанная инициализация при добавлении второго канала I 2 C на многоканальных чипах. Ошибка из серии «Я скопировал оттуда, где работало, а тут не работает».
Кстати, последняя ошибка встречается не столько при простом копировании кода, завязанного на работу через I 2 C, а сколько на его инициализацию. STM32 штука сложная и если невнимательно работать с Cube или писать инициализацию своими руками, то наверняка куда-то может затесаться мизерная ошибочка, которую замыленный глаз уже не в состоянии разглядеть. Вторая же ошибка по большей части связана с неверной (а зачастую с бездумной) инициализацией микроконтроллера, но присутствуют и особенности реализации (читай ошибки) в самом чипе. С ними (обнаруженными и признанными) и объясняется что делать в замечательном документе Errata sheet (ссылка внизу статьи).
В общем наилучшее, что мог создать коллективный разум, это код принудительной переинициализации функции I 2 C через HAL с дополнительными задержками:
/* USER CODE BEGIN SysInit */
HAL_RCC_I2C1_CLK_ENABLE();
HAL_Delay(100); HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(100);
__HAL_RCC_I2C1_RELEASE_RESET();
HAL_Delay(100);
/* USER CODE END SysInit */
В Arduino на STM32 данный блок так же можно применить, но он не помогает, по крайней мере, в моем случае. Пришлось еще немного пораскинуть мозгами и попытаться докопаться до причины проблемы, а потом попытаться ее решить. В моем случае обмен данными с EEPROM по I 2 C идет без каких-либо проблем. Данные читаются, записываются, никаких ошибок не возникает. Только вот в одном разе из 10 после аппаратной перезагрузки всей системы, EEPROM начинает выдавать совершенно левые данные, при этом никаких ошибок не возникает. С записью тоже в такие моменты не все гладко, проверить-то никак.
Как известно, чипы STM32 многофункциональны и многие из выводов микросхем могут быть использованы под различные функции. У многих микроконтроллеров, не только у STM32, после перезагрузки, некоторые выводы могут переключиться в так называемые неинициализированные состояния. Обычный софтверный разработчик, как правило не задумывается над тем, какой у него уровень на выводах микроконтроллера после его перезагрузки. Высокий? Низкий? Серединный? При использовании фирменного конфигуратора STM32Cube есть возможность настроить инициализацию выводов и некоторых других функций микроконтроллера путем относительно простого конфигурирования. Но данная процедура может быть опущена, а инициализацию можно провести позже, например, при процедуре вызова той или иной функции. Именно последним путем и пошли разработчики STM32Duino. При загрузке микроконтроллера происходит так называемая базовая инициализация функций микроконтроллера, ножки выводов принимают значения по умолчанию. А вот если с данной конкретной ножки требуется другая функция, то ее инициализация происходит при первом вызове соответствующей функции.
В чипах STM32 инициализация происходит очень быстро, ну сами чипы скоростные, это, во-первых, а во-вторых, загрузчик не тормозит загрузку пользовательского кода, так как вызывается при соответствующей аппаратной комбинации. Значит, проблема неинициализированных «ног», когда на них болтается неизвестно что, встает не в полный рост. А вот на других чипах микроконтроллеров, где загрузчик некоторое время ожидает подачу ему сигнала и только потом переходит на пользовательский код, проблема может существенно попортить жизнь. Представьте, что на такой «ноге», которая еще не определилась с уровнем своего сигнала, «висит» управляющий контакт реле. И вот на реле идет жуткая последовательность непонятных сигналов. Что ему делать? Дергаться туда-сюда, пока микроконтроллер не определиться со своим выводом?
Опытный читатель или электронщик, уже догадался, в чем изюминка порылась. Микросхема EEPROM возвращает неверные данные, а библиотека, работающая с I 2 C, говорит, что все нормально, ошибок нет. Суть нестабильного поведения кроется в следующем. На универсальных чипах STM32F103 многие из выводов многофункциональны. При неверной инициализации или отсутствии инициализации, на «ногах», подключенных к микросхеме EEPROM, может появиться произвольный сигнал, который «сведет с ума» саму микросхему EEPROM (команды на обмен данными с EEPROM та еще китайская азбука, куча условностей, задержек и прочего). Да, она будет как-то реагировать на команды, но вот выдавать данные может совсем не те, что должна. Повторная инициализация I 2 C в микроконтроллере ничего не даст, так как ведомое устройство уже не в себе и вывести его из себя можно только аппаратной перезагрузкой микросхемы EEPROM (перезагрузка микроконтроллера тут не помогает, по той же причине, что и переинициализация через HAL).
Именно такая ситуация возникла в моем случае. Проблема возникала случайным образом, но статистически она присутствовала. Если код инициализации Wire поместить ближе к началу программного кода, то вероятность возникновения ошибки уменьшается, если отодвинуть его куда-то подальше, то ошибка будет возникать чаще. И спастись от проблемы можно только аппаратным сбросом всего оборудования (передергиванием питания).
Так как же можно избавиться от проблемы «сумасшествия» ведомой микросхемы EEPROM? Очевидно, что нужно максимально быстро проинициализировать соответствующие терминалы ввода-вывода, дабы успеть в тот момент времени, пока EEPROM не начнет жить по своим собственным законам, повинуясь непонятным сигналам с микроконтроллера. Другими словами, подвинуть код инициализации Wire в самое начало программы. Но… Данный трюк не решает полностью описанное поведение EEPROM. Все еще остается вероятность отказа EEPROM (и опыты это подтверждают). Почему? Потому, что выполнение кода инициализации Wire занимает какое-то время, бесценные микросекунды, которых хватает на то, чтоб EEPROM удалились в мир грез и фантазий. Что в этом случае можно сделать?
pinMode(I2C_SCL, OUTPUT);
pinMode(I2C_SDA, OUTPUT);
Оказалось, что достаточно только проинициализировать порты микроконтроллера, ответственные за I 2 C как выходные цифровые выводы, как можно быстрее. В этом случае цифровое, а скорее аналоговое, шатание отменяется и невменяемость EEPROM тоже. В STM32Duino при инициализации пина как выходящего, он автоматически включается на низкий уровень. Если этого не происходит, например, поменялась идеология разработчиков фреймворка или вышла новая плата, на которой все не так, то дополнительно можно принудительно установить низкий уровень, что должно обеспечить нормальную работоспособность всей связки.
А что же до любителей HAL и особенно STM32Cube? Если работать только на HAL и не прибегать к Cube, как к средству конфигурирования, то проблема будет ровно такой же. Если не применить четкую инициализацию «ног» I 2 C как можно быстрее, то нормально с EEPROM не поработаешь. Впрочем, с Cube тоже не все так гладко как хотелось бы. Да, утилита помогает провести инициализацию микроконтроллера, которая сама по себе не отличается простотой. Но и тут могут быть нюансы. Во-первых, код инициализации I 2 C из Cube может быть выполнен в самую последнюю очередь, когда уже поздно, во-вторых, могут наступить и прочие конфликты, связанные с неверной инициализацией (Cube только выглядит просто, на самом деле без понимания туда лезть не стоит). Более подробно о потенциальных проблемах можно почитать в ссылках ниже.
- STM32 WRITE AND READ EEPROM OVER I2C BUS — статья детально разжевывающая методы обращения с EEPROM подключенным посредством I 2 C.
- STM32F10xx8 STM32F10xxB Errata sheet (medium-density device limitations) — бюллетень от STMicroelectronics описывающий возможные затруднения и способы борьбы с ними по различным блокам своих микроконтроллеров. Проблем работы с I 2 C в документе указано аж 7.
- STM32 — I2C — HAL_BUSY — статья что делать, если возникает Busy.
Опубликовано 12.09.2020 автором kvv в следующих категориях:
DIYSoftстатья
Источник



