rp - how fast is the I2C running, 400KHz?
PMBus on STM32F103CBT6... ???
(27 posts) (3 voices)-
Posted 1 year ago #
-
gbulmer - Yup! Some math (this should be right): 6 bytes in this "read voltage" transaction (Addr, Cmd, Addr, RxByte, RxByte, PECByte). 400kHz, so 2.5 uS per cycle, meaning 2.5 uS per bit. The easy way, it's 5 bytes x 8 bits x 2.5 uS (if the start / ack / stop aren't counted) - 120 uS. So, about 140 uS means about 20uS left - start, stop, ack. 2 start, 6 ack (I think), 20 uS (8 * 2.5 uS).
It was about 120 uS for 5 bytes (1 RxByte instead of two, just reading the version), so what I'm getting is consistent.
Going through the math though, it seems like there's no code overhead - which isn't quite true, which makes me question the actual data stream (ex, am I actually generating a STOP, or am I signaling done before I even get the STOP?).
So, more testing is needed before I can say yes, that's what it is. Seems to me it should be a bit longer...
Posted 1 year ago # -
rp - at about 400KHz, you could experiment with using a Maple as a logic probe/analyzer.
I remembered this thread:
http://forums.leaflabs.com/topic.php?id=74071#post-104826
where mlundinse had ported the source of an Arduino logic analyser. I can't find a link pointing to the port, though. It probably isn't worth the effort unless the code is found, or you're very keen to have it work.Posted 1 year ago # -
gbulmer - Ah, I thought you were wondering about the speed itself... :) Hmmmm... I read the thread, and you're right, there's no link to the port. Sure would be handy to have that code available. I wonder how much trouble it would be. I was staring at the $150 logic analyzer over on Sparkfun, wondering if it would be cost effective to just buy it vs spending the time to do something else. If mlundinse had posted the code, I'd surely try that first!
What I'm finding out is that ST's implementation of I2C and their documentation leaves a lot to be desired. I'm fairly certain I'm not generating PEC on Tx (though I'm setting the PEC bit in CR1 after the TxE corresponding to the last byte sent - as the RM says to do). I'm guessing PEC is working on Rx, but I'm not entirely sure that it's even being transmitted by the DC to DC converter or received by the STM32. The ISR doesn't seem to be firing as expected for SMBus Tx, still trying to sort that out.
What would be helpful is a block diagram of the I2C hardware internals, something more elaborate than what's in the RM - or a suitable write up on it.
I'll keep plugging away at it, trying to figure out what ST is doing in there...
Not sure what to do about analyzing the signals, though.
Yo, mlundinse, got some code? :)
Posted 1 year ago # -
rp - I2C on STM32F1xx seems to be the worst peripheral, and appears to have been changed in newer STM32 family members.
Have you looked at the Errata:
http://www.st.com/st-web-ui/static/active/en/resource/technical/document/errata_sheet/CD00190234.pdfThere are several errors noted, with some workarounds.
It would be kind and helpful if mlundinse released code for the port of the logic analyser, even if it isn't as finished as they might like.
(Full disclosure: I am not a member of LeafLabs staff.)
Posted 1 year ago # -
It's pretty ugly, that's for sure. I read the errata - there are a few issues in there, mostly applying to "method 2", when I2C is not highest priority - which is OK in my case, as I need I2C as highest for PWM updates and timely IMU9150 reads (gyro correction of servo positions). I noticed the SMBus issue, can't nack a bad transaction - which isn't what the RM says (PEC, RM 26.3.8, "in reception" paragraph)!! I question if ST even knows how to make this thing work properly. I've looked over their CPAL libraries, and all I see from an external use standpoint is polling for state throughout the I2C transaction - not very useful, IMO - I must say, however, I didn't read very far into the CPAL code.
But, one thing they didn't mention for the SMBus issue as a solution is to check the device's internal comm errors - no extra pins / protocols are needed for that. If the Rx (of the comm errors register) fails, one could safely assume communiction was bad and the originial command didn't go anyway. If the Rx succeeds (doesn't generate an error event), comm status in the device can be read back, with PEC at the STM32 indicating if the Rx was successful, then errors can be checked for. So there's another solution I didn't find in ST's documentation. Or, if I command a power off or voltage change, I should be able to read back the voltage to verify (which isn't a bad idea, regardless of NACK).
I keep seeing a trend - in the libmaple code, in the ST examples - to disable the IRQs. To me, that just seems like a bad way to do things. Shouldn't the IRQs be left active, left alone to handle events as they occur? If the ISR fires, it's for a reason, right? I know it's a workaround for hardware problems, but I am trying NOT to do that (though the code includes a few "disable_irq" calls at the moment). What I found when disabling the ISR upon completion of a transaction was that I wasn't able to reset the last TXE / RXnE / BTF (though it seems they will go on forever), so enabling the ISR invoked TXE / RXnE / BTF on start of the next I2C transaction (sitting and waiting from the last event). So, what I did as a shortcut was to clear the TXE / RXnE / BTF and jump out of the ISR if I'm on the first "step" in the transaction and SR1 indicates something other than just SB. There's got to be a better way to handle this!!
I'll keep beating on it, got a few days here to further things along. Meanwhile, I have a few Maple Mini's to repair, seems I had a loose ground that helped fry a few STM32's. Then, I can fix the loose ground, solder up some STM32F103CBT6's I got from Digikey yesterday, and get back to work on the code. At last test, I think I had tx nailed, but I won't know for certain until I have some Maple Mini's to work with again - I'm not willing to test on my custom board, I'd rather foul up a Maple Mini than the custom board!! Once the STM32's are soldered up, I'll need to reload the LeafLabs bootloader, which I've done before. Hopefully it won't take too long to clean up the pads (removed the chips Thurs) and get just enough paste on there to pin them down again.
I know LL would be happy to have me buy more, but let's face it - cheaper to repair if possible - and it gives me more experience on reflowing chips in tight spaces.
Of the code I'm working on, I'd be fine giving it to LL when done, but I haven't exactly followed their coding standards (spacing, 80 col, comment styles, etc). Thoroughly commented, though. I figure if they (or someone else) wants it bad enough, they'll work it over - which is a minor thing compared to the time I've spent messing with it!!
I could go without PMBus to my DC to DC converters, but doing so limits some features I really want to implement, and requires me to forego intelligent shutdown of the system. It's a set of features I'd call "mandatory", so on I go.
Posted 1 year ago # -
rp - Thank you very much for sharing your experiments, progress and analysis. I hope you get it cracked, as I2C very useful.
Polling instead of interrupts seems like a potential waste of significant CPU time; polling for the entire transfer of two bytes at 400KHz is almost 3000 instructions. Clearly, this is often acceptable as many I2C devices are very slow, but it seems irritating.
Posted 1 year ago # -
Argh! Got logged out before I posted, post lost...
gbulmer - Thanks, and no problem - I like to type. ;)
Yeah, I was pretty surprised to get a good look at the code and see it was just waiting around for the ISR to say it was done. I think of an ISR as like a thread in an OS, you should do what you need to in the ISR and let the system do whatever else it needs to do when the ISR is not running. Since the ISR may not fire, the actual function the ISR handles should be checked in the main code (ex, timeout) - at least, that's the way I'm building this. At the moment, it's still got wait_for_state_change, but that's just until I work out the details in the transactions and ISR (which is almost done). Right now, I could let go of wait_for_state_change and just poll for XFER_DONE from the main loop, but there's no timeout if it fails - yet. This'll be a function, returning something like "complete", "error / timeout", "busy", and if it times out, it'll stop whatever's in progress or pending, ready for a fresh message (ex, if timeout, can retry immediately or do other).
At first glance, it seems USART is the same way. I'll likely change that to something to handle my PC to Control Board protocol, much like the I2C code - but it should be easier, less involved with serial comms from what I saw in the RM.
I should mention - my 4 Maple Mini's have new brains, bootloaders loaded, ready to go. I tested my last version of PMBus / SMBus Tx w/ PEC (when the Maple Mini fried due to a bad ground before I could check the result), and IT WORKS. I called for a "margin down", and the converter did so (and it won't without proper PEC), no errors from I2C either. So, Tx with PEC is good. Rx needs to be adjusted, but I know where and how, so that's quick. Then, I'll work on the basic I2C transactions - essentially the same as what I have for PMBus / SMBus, but less. In all, I need to wrap some things up nice and neat, write in the transaction structures and functions to modify them (maybe with some Arduino-esque functions just to keep it newbie friendly). At the moment, the transactions live in the Setup of the test sketch (the hex is meaningless unless coupled with the "magic spreadsheet", will make more sense with functions to modify the transactions).
To elaborate, a "transaction" (my term) is based on the firing of the ISR, incrementing through an array of uint32 on each firing (aside from the TXE / RXnE / BTF workaround mentioned earlier). Each uint32 is: 0->7 bits for data (rx or tx or Addr <<1 | R/w, if used), 8->15 bits are the expected low byte of SR1, 16+ are flags for actions performed within the ISR (ex, repeat start, set PEC, Send Stop, En ACK, Dis ACK, read SR2, Write DR, Read DR, etc). A transaction is the sequence of the expected SR1 state on ISR call, related data (in or out), and actions (single or multiple) to take. If the low byte of SR1 wasn't what was expected, something went wrong, bail. If the time between the last event (intialization of transfer OR ISR firing and expected low byte of SR1) and when the Status function is checked from the main loop exceeds some duration, all is cancelled / reset, and on it goes.
Also good, I'm getting better with C. :)
Posted 1 year ago # -
More success...
Just found two "oops" code mistakes in the last few hours, and sure enough, once those were resolved, I was able to remove the BTF / TXE / RXnE shortcut I had in there. Also fixed a problem I had on 2nd attempt at tx or rx.
And, there is no need to disable the IRQ flags (something I was trying to avoid), they can stay active the whole time, and it doesn't misbehave. All I have to do is set a transaction (send or receive) and set SB (start), and the ISR does the rest.
Operation is very clean at this point (but the code isn't, lots of testing garbage to take out), it's doing what I had intended. I fought this thing all day long and into the wee hours, thinking I was overlooking something - finally found it.
So, SMBus / PMBus Rx and Tx are working like they should (repeat start, hardware PEC, and all), and the ISR is working exactly as it should.
Interesting to note, switching on the SMBUS bit only caused problems - couldn't run at 400khz, had to back it down to 100khz. Not sure if there's a register that could be tweaked for the SMBUS timing restrictions, but it works fine without (not using SMBalert pins, ARP, etc). And fyi, PEC can be used without having to set the SMBUS bit.
I'll keep working on it (regular I2C, code cleanup, etc), things should go more smoothly from here on out.
Posted 1 year ago # -
After cleaning things up, I have some numbers:
* For 6 bytes (Rx, repeat start), including PEC, 174 uS at 400 khz (micros()).
* For 4 bytes (Tx, no repeat start), including PEC, 100 uS at 400 khz (micros()).All works, nothing broken on refactoring / cleanup. I was able to load up the Rx sketch, read 6.05v repeatedly, reset the board, loaded up the Tx sketch, issued the margin down repeatedly, reset the board, loaded the Rx sketch again, read 5.45v repeatedly. Actually, I can see some jitter in the voltage readings, but that's expected. No PEC errors, and I've seen them in testing (ex, stop in the wrong place). I'm fairly certain I'm setting stop (the ISR quits firing), so I doubt I'll test it with a DSO / logic analyzer / etc.
Next step is to do both Tx and Rx back to back without going through reset / re-enable. Being that the state of the ISR / peripheral is stable, I don't expect any problems.
One thing I want to do is queue up transactions so there's always data ready to go out over the I2C bus (this is needed for PWM, I'll be doing a lot of position updates over I2C). I might need to add a check for the MSL bit following stop, otherwise, I'll have a queue where I can check the status of the transaction from the main code and remove the msg before queuing next.
And WOW, is USBSerial SLOW!! :) I know why, and that's why I have a CP2103 USB to UART chip on my custom board.
***
Just tested, and yes, it works with back-to-back rx / tx / rx (repeated). Woohoo!!
Posted 1 year ago # -
More info... So far (running about 20 mins straight), the ISR works with a PCA9685 PWM controller. I have a servo driven by the PCA9685 moving from end to end and back over time, then read voltage / margin voltage / read voltage from the DC to DC converter and repeat.
In actuality, I won't have the PCA9685 on the same bus as the DC to DC converters, the 2 PCA9685's will get their own bus. The DC to DC converters will be on the same bus as the MPU-9150, though. No breakout board for the MPU-9150, so next round of testing (perhaps later today) will be on my custom board. I'll also be able to work on simultaneous I2C transactions (ex, read / write to the converters and the MPU-9150 on I2C1, while streaming positions out to the PCA9685's over I2C2). I'll also be able see how much CPU time I have when the ISR isn't firing while I2C communication is ongoing.
So, I have PMBus / SMBus devices working with a regular I2C device at 400khz on the same I2C bus with no issues. This exactly what I was hoping for - so far, so good.
Posted 1 year ago # -
I'm thinking of doing something like what's in ring_buffer for type i2c_msg for queing I2C transactions. Should work fine. Then, transactions can be picked off the end when processed by the main code, new ones can be added as long as the buffer isn't full (of pending, in-process, or completed transactions not yet processed by main()).
In considering how to save space, I'm wondering how much I can simplify transaction halding in the ISR - I don't want to overdo it. What I have right now is very flexible, but somewhat tedious to set up - and depending on how many different forms of I2C communication are required, could take a fair bit of RAM. Hmmm...
Already thinking ahead regarding USART message handling - I should be able to use the CRC calculation function with USART, that's the only place I think I'll need it. The PC host interface protocol I've been working on is one-by-one, each requiring a response to ensure successful tx / rx, no response if tx fail (timeout on both sides, re-send). Still, I wonder about something like porting ZModem to the STM32 instead. Probably too big, though.
Posted 1 year ago #
Reply
You must log in to post.