You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For over a month I have been trying to get a simple I2C program running, and for over a month I have received exactly one error: BUS_BUSY | ARBITRATION_LOST which is likely due to the I2C controller flag TRANSMIT_DATA being high.
I don't know enough about the I2C protocol to understand it, and I do not have a digital analyzer or oscilloscope to measure the lines. Hardware looks fine, I am using a Teensy 4.1 and it is connected to an SSD1306 OLED will pullups.
Here is what I know.
TRANSMIT_DATA flag is stuck high from initialization one way or another, have never seen it low in any case no matter what condition at all. Cannot be cleared with any of the following:
Using reset_controller()
Using clear_fifo()
Using clear_controller_status(ControllerStatus::all())
Trying thousands of reset attempts in a while loop out of desperation
When attempting I2C write:
System hangs (perpetual LED of sisyphus stops blinking)
Need to remove power from the OLED before anything runs again
Afterwards the USB logger shows ARBITRATION_LOST and BUS_BUSY errors from the I2C write_byte function
FIFO shows txcount of 2 after failed write sometimes ??
Hardware Setup
Teensy 4.1
SSD1306 OLED Display
I2C Address: 0x3C
Using LPI2C3 peripheral on pin 16 for SCL and pin 17 for SDA, LPI2C1 has the exact same issues
Both pins configured with internal 100k pull-ups
Attempted Solutions
Every combination of resetting things in different orders (e.g., clear FIFO then I2C controller, etc.)
Tried to clear status flags in different orders as well
Tried 100KHz and 400KHz each numerous times
Explicit pin configuration before I2C initialization
Multiple reset attempts during initialization
Read through most of the embedded-hal and imxrt-hal source code
Read each of their respective doc books front and back and could not find any issues or implementations
Implementing 2 I2C examples listed in the referenced code, neither worked.
I am about to give up on this insultingly simple problem. Please anybody help, if anything to see a different error. I have spent the better part of 60+ hours on this.
#![no_std]#![no_main]#![deny(unsafe_code)]use teensy4_panic as _;mod drivers;#[rtic::app(device = teensy4_bsp, peripherals = true, dispatchers = [KPP])]mod app {// Teensy 4.0 and 4.1 board imports// - Teensy 4.1 is `teensy_board::t41`// - Teensy 4.0 is `teensy_board::t40`use teensy4_bsp as bsp;// Target System on a Module (SoM)// IMXRT HAL importsuse imxrt_log as logging;// RTIC imports// TODO: "*" is for `.millis()`, need to changeuse rtic_monotonics::systick::{Systick,*};usecrate::drivers;constPIN_PULLUP: bsp::hal::iomuxc::Config = bsp::hal::iomuxc::Config::zero().set_pull_keeper(Some(bsp::hal::iomuxc::PullKeeper::Pullup100k));constPIN_PULLDN: bsp::hal::iomuxc::Config = bsp::hal::iomuxc::Config::zero().set_pull_keeper(Some(bsp::hal::iomuxc::PullKeeper::Pulldown100k));/// Shared resources across different tasks/// RTIC ensures safe concurrent data access without/// manually having to sync tasks, primitives, etc.#[shared]structSharedResources{}/// These resources are local to individual tasks.#[local]structLocalResources{/// The LED pin is typically GPIO13led: bsp::board::Led,// i2c_port: bsp::board::Lpi2c3,i2c_instance: drivers::I2C<bsp::board::Lpi2c3>,/// Helps to control USB loggingpoller: logging::Poller,}#[init]fninit(cx: init::Context) -> (SharedResources,LocalResources){let bsp::board::Resources{
usb,
lpi2c3,mut pins,mut gpio2,
..
} = bsp::board::t41(cx.device);// Initialize SystickSystick::start(
cx.core.SYST,
bsp::board::ARM_FREQUENCY,
rtic_monotonics::create_systick_token!(),);// Initialize GPIOlet pin_led = gpio2.output(pins.p13);// Initialize an I2C interface on port 1// SCL, LPI2C3 is pin 16, LPI2C1 is pin 19// SDA, LPI2C3 is pin 17, LPI2C1 is pin 18
bsp::hal::iomuxc::configure(&mut pins.p16,PIN_PULLUP);// SCL
bsp::hal::iomuxc::configure(&mut pins.p17,PIN_PULLUP);// SDAlet i2c_port = bsp::board::lpi2c(
lpi2c3,
pins.p16,// SCL
pins.p17,// SDA// bsp::board::Lpi2cClockSpeed::KHz100
bsp::board::Lpi2cClockSpeed::KHz400);let i2c_instance = drivers::I2C::new(i2c_port);// Initialize a poller for USB serial monitoring / logginglet poller: logging::Poller = logging::log::usbd(usb, logging::Interrupts::Enabled).unwrap();// Task constant configs// Is this a bad idea?constLED_MS_DELAY:u32 = 500;constLOG_MS_DELAY:u32 = 1000;//let end_status_controller = i2c_port.controller_status();//let end_status_fifo = i2c_port.controller_fifo_status();// The SoM has now been initialized// We can begin scheduling tasks to run
blink::spawn(LED_MS_DELAY).unwrap();
test_usb_logging::spawn(LOG_MS_DELAY).unwrap();// port_i2c_test::spawn().unwrap();
test_i2c_new_hal::spawn().unwrap();// Return local and shared resources(SharedResources{},LocalResources{led: pin_led,// i2c_port,
i2c_instance,
poller,})}/// Basic LED blink example on GPIO13#[task(local = [led])]asyncfnblink(cx: blink::Context,ms_delay:u32){loop{
cx.local.led.toggle();Systick::delay(ms_delay.millis()).await;}}/// Test how multiple tasks all trying to log via USB will work if it does/// Updated: Seems to work great at least with text logging#[task()]asyncfntest_usb_logging(_cx: test_usb_logging::Context,ms_delay:u32){letmut count:u32 = 0;loop{
log::info!("Test count: {count}");
count += 1;Systick::delay(ms_delay.millis()).await;}}#[task(local = [i2c_instance])]asyncfntest_i2c_new_hal(cx: test_i2c_new_hal::Context){Systick::delay((5000asu32).millis()).await;let i2c_instance = cx.local.i2c_instance;constOLED_ADDR:u8 = 0x3C;constMAX_RESET_ATTEMPTS:u16 = 10000;
log::info!("Initial status: {:?}", i2c_instance.port().controller_status());// Try to clear TRANSMIT_DATAletmut attempts = 0;while i2c_instance.port().controller_status().contains(bsp::hal::lpi2c::ControllerStatus::TRANSMIT_DATA){
log::info!("Attempt {} to clear TRANSMIT_DATA", attempts);
i2c_instance.port().reset_controller();
i2c_instance.port().clear_fifo();
i2c_instance.port().clear_controller_status(bsp::hal::lpi2c::ControllerStatus::all());Systick::delay((1asu32).millis()).await;
attempts += 1;if attempts >= MAX_RESET_ATTEMPTS{
log::error!("Failed to clear TRANSMIT_DATA after {} attempts", attempts);break;}}// Only try write if we cleared TRANSMIT_DATAif !i2c_instance.port().controller_status().contains(bsp::hal::lpi2c::ControllerStatus::TRANSMIT_DATA){
log::info!("TRANSMIT_DATA cleared, attempting write...");match i2c_instance.write_byte(OLED_ADDR,&[0x00]){Ok(_) => log::info!("OLED write successful"),Err(err) => {
log::error!("OLED write failed: {err:?}");
log::info!("Status after error: {:?}", i2c_instance.port().controller_status());
log::info!("FIFO after error: {:?}", i2c_instance.port().controller_fifo_status());}}}loop{Systick::delay((1000asu32).millis()).await;
log::info!("Current status: {:?}", i2c_instance.port().controller_status());
log::info!("Current FIFO: {:?}", i2c_instance.port().controller_fifo_status());}}/// Runs when the USB1 interrupt activates/// Poll the logger to control the logging (e.g., `log::info!(...);`)/// This seems to work fine with text logging if multiple tasks call it at once////// To view USB logging results use a serial logger/// - The "Serial Monitor" extension on VSCode works perfect for this/// - Make sure you are connected to your devices correct COM port (e.g., COM3)/// - Baud rate @ 115200 works well////// Used additional help from this USB logging example:/// (Very similar to the original LED blink program / template)/// https://github.com/mciantyre/teensy4-rs/blob/master/examples/rtic_usb_log.rs#[task(binds = USB_OTG1, local = [poller])]fnusb_interrupt(cx: usb_interrupt::Context){
cx.local.poller.poll();}}
/drivers/i2c.rs
use teensy4_bsp as bsp;use embedded_hal;pubstructI2C<I2CPORT>{i2c:I2CPORT,}// Because implementing return and write sizes of byte, word, dword, etc.// for `xxxx_vec::<data length>(address)` simply changes the generic `N`,// these macros do it all for us.macro_rules! impl_write {($name:ident, $size:literal) => {/// Generated implementation of `write_vec` by the `impl_write!` macropubfn $name(&mutself, addr:u8, data:&[u8; $size]) -> Result<(),I2CPORT::Error> {self.write_vec::<$size>(addr, data)}}}macro_rules! impl_read {($name:ident, $size:literal) => {/// Generated implementation of `read_vec` by the `impl_read!` macropubfn $name(&mutself, addr:u8) -> Result<[u8; $size],I2CPORT::Error> {self.read_vec::<$size>(addr)}}}macro_rules! impl_write_read {($name:ident, $size:literal) => {/// Generated implementation of `write_read_vec` by the `impl_write_read!` macropubfn $name(&mutself, addr:u8, data:&[u8; $size]) -> Result<[u8; $size],I2CPORT::Error> {self.write_read_vec::<$size>(addr, data)}}}impl<I2CPORT: embedded_hal::i2c::I2c>I2C<I2CPORT>{pubfnnew(i2c:I2CPORT) -> Self{Self{ i2c }}pubfnport(&mutself) -> &mutI2CPORT{&mutself.i2c}/// Generic N length I2C data write functionality/// Write `write_vec::<your_len_here>`/// Works with embedded-hal implementations of I2C such as/// - LPI2Cx for the imxrt-hal/// - (soon the STM32H7xx-hal?)pubfnwrite_vec<constN:usize>(&mutself,addr:u8,data:&[u8;N]) -> Result<(),I2CPORT::Error>{self.i2c.write(addr, data)?;Ok(())}impl_write!(write_byte,1);impl_write!(write_word,2);impl_write!(write_dword,4);/// Generic N length I2C data read functionality/// `read_vec::<your_len_here>(addr)`/// /// Works with embedded-hal implementations of I2C such as/// - LPI2Cx for the imxrt-hal/// - (soon the STM32H7xx-hal?)pubfnread_vec<constN:usize>(&mutself,addr:u8) -> Result<[u8;N],I2CPORT::Error>{letmut return_buffer = [0u8;N];// Regurgitate an array.self.i2c.read(addr,&mut return_buffer)?;Ok(return_buffer)}impl_read!(read_byte,1);impl_read!(read_word,2);impl_read!(read_dword,4);/// Generic N length I2C write then read from the same address functionality/// Works with embedded-hal implementations of I2C such as/// - LPI2Cx for the imxrt-hal/// - (soon the STM32H7xx-hal?)pubfnwrite_read_vec<constN:usize>(&mutself,addr:u8,data:&[u8;N]) -> Result<[u8;N],I2CPORT::Error>{self.write_vec::<N>(addr, data)?;Ok(self.read_vec::<N>(addr).unwrap())}impl_write_read!(write_read_byte,1);impl_write_read!(write_read_word,2);impl_write_read!(write_read_dword,4);}
cargo.toml
[package]
name = "agony"version = "0.1.0"edition = "2021"
[dependencies]
# MCU-architecture-specificimxrt-log = { version = "0.1", default-features = false, features = ["log", "usbd"] }
[dependencies.log]
version = "0.4"features = ["max_level_debug", "release_max_level_info"]
[dependencies.teensy4-bsp]
version = "0.5"features = ["rt"] # Use the BSP's runtime, lets you write `main()`
[dependencies.teensy4-panic]
version = "0.2"features = ["log"]
[dependencies.embedded-hal]
version = "1.0.0"################################ RTOS framework dependencies ################################
[dependencies.rtic]
version = "2.0.1"features = ["thumbv7-backend"]
[dependencies.rtic-monotonics]
version = "1.0.0"# default-features = falsefeatures = ["cortex-m-systick"]
############################ Build-time dependencies ############################
[build-dependencies]
toml = "0.8.19"# Set to 0 to optimize build times, no optimizations# Set to 3 for all optimizations# Set to "z" to optimize build size w/o loop vectorization
[profile.release.build-override]
# opt-level = 0 # No optimizations# opt-level = 1 # Few optimizations# opt-level = 2 # Some optimizations# opt-level = 3 # All optimizations (slowest build time)# opt-level = "s" # Smallest binary sizeopt-level = "z"# Optimize for build size w/o loop vectorization# [target.<architecture-triple>]# runner = 'probe-rs run --chip <chip-name>'
The text was updated successfully, but these errors were encountered:
For over a month I have been trying to get a simple I2C program running, and for over a month I have received exactly one error:
BUS_BUSY | ARBITRATION_LOST
which is likely due to the I2C controller flagTRANSMIT_DATA
being high.I don't know enough about the I2C protocol to understand it, and I do not have a digital analyzer or oscilloscope to measure the lines. Hardware looks fine, I am using a Teensy 4.1 and it is connected to an SSD1306 OLED will pullups.
Here is what I know.
TRANSMIT_DATA flag is stuck high from initialization one way or another, have never seen it low in any case no matter what condition at all. Cannot be cleared with any of the following:
When attempting I2C write:
Hardware Setup
Attempted Solutions
I am about to give up on this insultingly simple problem. Please anybody help, if anything to see a different error. I have spent the better part of 60+ hours on this.
Referenced code:
https://github.com/RoboJackets/robocup-rustware/blob/2fab2e23d003bdc0cc6d33cc6efbb2a6bbc87f69/control/examples/scan_i2c.rs
https://github.com/SharpCoder/teensycore/blob/8bd5535aacf34af972a07a849f02c8160741a96f/src/i2c.rs
every single example: https://github.com/mciantyre/teensy4-rs/tree/master/examples
uses an old version of embedded-hal i could not determine to get working: https://github.com/imxrt-rs/imxrt-hal/blob/main/examples/hal_i2c_mpu9250.rs
The entirety of my code is listed below.
main.rs
/drivers/i2c.rs
cargo.toml
The text was updated successfully, but these errors were encountered: