-
Notifications
You must be signed in to change notification settings - Fork 1k
Add SUBGHZSPI support to SPI library. #1839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Looks ok to me. I had not realized that actual pins were involved (before I thought that some virtual dummy pins were added just to signal to the SPI code that SUBGHZ_SPI was to be used), but now I see that there are actual pins attached to the SUBGHZ_SPI module. IICU these are not regular SPI pins (in particular MISO seems to be output-only), so I do not think SUBGHZ_SPI can be used to drive an external SPI slave, right? I do wonder if the API offered now (passing these debug pins to select SUBGHZ_SPI, but by default not actually using those pins but only use them when calling |
This PR is not ready as I want made some tests. |
Below is my 'gist' on how to make the SUBGHZSPI radio working with basicmac library:
See this reference for more details. |
Hey, cool! Once this PR is merged, I'd be interested in merging your work into basicmac (where I happen to also have write access ;-P). |
3b92472
to
fae5185
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some comments inline.
I'm also wondering what pin number is a sketch supposed to pass to various begin/transfer functions? Currently it can be any non-zero number, but that seems a bit broad (but if so, it should probably be at least documented in a comment on the class)?
It is documented here: Mainly:
With SubGHZSPI, 2 last cases are the same as there is no hardware CS. |
c4bb50b
to
3c572df
Compare
Hello @fpistm any advice on using it to communicate with the SubGHZ IP? #include <SPI.h>
#define SubGHZ_NOP 0x00
#define SubGHZ_READ_REGISTER 0x1D
#define SubGHZ_SIZE_READ_REGISTER 4
#define SubGHZ_REG_LR_SYNCWORD_MSB 0x07
#define SubGHZ_REG_LR_SYNCWORD_LSB 0x40
#define SubGHZ_SIZE_REG_LR_SYNCWORD 2
void setup() {
Serial.begin(921600);
delay(1000);
SubGHZ_SPI.begin(!CS_PIN_CONTROLLED_BY_USER);
uint8_t buffer[SubGHZ_SIZE_READ_REGISTER + SubGHZ_SIZE_REG_LR_SYNCWORD] = {SubGHZ_READ_REGISTER, SubGHZ_REG_LR_SYNCWORD_MSB, SubGHZ_REG_LR_SYNCWORD_LSB, SubGHZ_NOP, SubGHZ_NOP, SubGHZ_NOP};
Serial.print("Command: 0x");
for (uint8_t i; i<SubGHZ_SIZE_READ_REGISTER + SubGHZ_SIZE_REG_LR_SYNCWORD; i++) {
Serial.printf("%.2X ", buffer[i]);
}
Serial.println();
Serial.print("Result: 0x");
SubGHZ_SPI.transfer(!CS_PIN_CONTROLLED_BY_USER, buffer, 4);
for (int i; i<SubGHZ_SIZE_READ_REGISTER + SubGHZ_SIZE_REG_LR_SYNCWORD; i++) {
Serial.printf("%.2X ", buffer[i]);
}
Serial.println();
}
void loop() {} Sorry if it is a basic question, last time I used Arduino SPI was a while ago... @matthijskooijman long time no see :) Glad that you are still playing with LoRa! |
Hi @Oliv4945 |
Hm, I'm not sure I understand entirely yet, so this post is me trying to make sense of things. Note that I personally do not really care about letting the SPI library control the CS pin, since it offers less control and clarity about when the CS pin is asserted exactly (and it seems this is also inconsistent or bugged currently, see below). However, if we offer this feature, at least it should be consistent.
In regular SPI library, this is done by not passing a CS pin to the constructor, and then not passing any pin numbers to the begin/begintransaction/transfer functions (or passing
In regular SPI library, this is done by not passing a CS pin to the constructor, and passing the pin to be controlled to begin/begintransaction/transfer functions.
In regular SPI library, this is done by passing a CS pin to the constructor. The pin value passed to begin/begintransaction/transfer is ignored. For SubGHZ_SPI, case 1 (from the user perspective) ends up passing For SubGHZ_SPI, case 2 (from the user perspective), this ends up controlling the NSS virtual pin, regardless of which pin is passed. This seems a bit confusing to me, it would make sense if this only happened when passing For SubGHZ_SPI, case 3 (from the user perspective) ends up behaving almost exactly like case 1 (since there It seems this interface is a bit confusing, though I'm not sure what would be better. From the user perspective, I guess there's basically two cases: Where the use controls the internal NSS signal manually (which works fine as case 1 now), and where the SPI library automatically controls it (which I guess feels more like case 3 than case 2, except that I think the timing of NSS switches is more like case 2 than case 3). Maybe the API to trigger automatic NSS control should be neither case 2 or 3, but be some SubGHZ_SPI-specific API? I also wonder if it makes sense to have a SubGHZ_SPI constructor that accepts pins at all, though. Normally, the pins passed are passed through the pin maps to be checked, so you can only pass pins that are actually connected to the SPI block. However, for SubGHZ_SPI, this pin map is skipped (because of the So maybe the SUBGHZSPIClass constructor with arguments can just be removed? This also makes (maybe) it a little more clear that case 3 (hardware control of NSS) is not supported (though I guess directly controlling the internal NSS signal might be more similar to case 3 than case 2, maybe...). NSS hardware control bugged?This is probably off-topic, but I noticed this in the code and want to at least mention this. When using NSS hardware control (case 3), according to the reference manual the NSS line is asserted when the SPI unit is enabled ( There also seems to be a discrepancy between case 2 and 3 in terms of timing: For case 2, the NSS pin is asserted for each transfer separately (which can be each byte, every 2 bytes for Finally, case 3 is now selected by the constructor, so you cannot mix that with other cases (e.g. have one slave where the NSS is controlled by hardware and another where the NSS is controlled manually), but I guess that is fine, since then you can just as well control everything manually (except when mixing libraries using the same SPI bus with different expectations, but I suspect most if not all libraries implement case 1 anyway). Arduino APIAnother thing to wonder is what the official Arduino APIs for this are. I haven't checked yet, but I'm pretty sure AVR only supports case 1 (with the same API). Checking SAMD I seem that is also case 1 only, but SAM supports hardware NSS control (but the API is more like case 2 rather than case 3, since a single SPI block has multiple channels/NSS pins it can control). I haven't looked into detail to when NSS is asserted, but I suspect that there might also be some inconsistency there... |
@fpistm As we discussed privately before, I've taken a stab at another class to control the SubGhz internal signals (NSS, Busy, Reset) and manage the radio interrupt. See matthijskooijman@1ca571f (single commit on top of this PR). That class now also includes the SPI object for the radio that was still a global in your PR. I'm not sure where this code should live exactly - it seems largely unrelated to SPI, so the SPI library is probably not the right place. So maybe in a library of its own, then? @Oliv4945 I suspect the radio might be in reset by default, so you might need to clear the reset first. See the |
In fact case 2 and 3 are the same as there is no hardware NSS. It is only to be consistent with SPI API. I do not want make dedicated SUBGHZSPI API. I see no good reason to diverge from default API.
Right, it is only for
Well maybe. Don't we fallback to SPI constructor in that case ? Don't remember this. |
Hi @matthijskooijman Note: Default instance: |
eb1775b
to
e94e0f1
Compare
@fpistm I've updated this PR, with the following changes to the SPI library:
I've put the first two in a fixup commit for easy review, this commit must be squashed before merging! Additionally, I've added a commit that adds my SubGhz object, now in a separate library as discussed, adding a README, keywords.txt, library.properties and an example sketch. I've changed the pin API to use bools instead of LOW/HIGH, I think that's the (marginally) better approach after all. I've also added an I've updated our (private) RadioLib prototype branch to use this new SubGhz library, that works well for me now. Next I'll clean up that branch and produce a proper public PR from that. |
e94e0f1
to
7319b31
Compare
Thanks @matthijskooijman I've pushed a change to pio build to avoid issue raised by the ci. (required to rebase on top of main) I agree for the fixup you can merge it. Pay attention to astyle for this commit. |
I've pushed again to fix astyle and codespell. Continuous integration still fails, since this now tries to compile the SubGhz library example for non-wl boards, which fails. I guess it would be useful to include this example in the CI build, but I'm not entirely sure what the best approach is (we could run |
Signed-off-by: Frederic Pillon <frederic.pillon@st.com>
Signed-off-by: Frederic Pillon <frederic.pillon@st.com>
Signed-off-by: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Co-authored-by: Frederic Pillon <frederic.pillon@st.com>
This turns SPISettings into a "literal type" and allows variables using it to be constexpr to guarantee the constructor is executed at compiletime. This requires (with C++11) that all variables are set using initializers instead of assignments and that the regular if cascade is replaced by a ternary if. It also requires explicit initializers for all values (omitted variables were previously initialized to zero anyway).
7319b31
to
20fd791
Compare
Seems our comments crossed. Ignoring the SubGhz library seems ok, maybe it can be added to the test build later. :-) I've rebased on main and merged the fixup (it was 490afe0 if you want to review it again). |
It can be ignored now. Even if it is not existing it should not be an issue. Thanks for the quick update. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @matthijskooijman the reset line did hte trick for me :)
I wrote minor changes suggestions
This allows accessing some of the signals internally connected to the SubGhz radio (that would have been GPIO signals if the radio was external). This also allocates and exposes the SPI object connected to the SubGhz radio block and handles attaching handlers to the radio interrupt. Note that the DIO signals are *not* exposed, since there is no way to read them directly (and indirectly reading them through the IRQ pending flag does not work in all cases).
8c74767
to
b192b02
Compare
Thanks @matthijskooijman LGTM even if PIO is CI is broken. |
This PR supersedes #1803.
It adds
DEBUG_SUBGHZSPI_*
pins support.To ease usage some alias have been added:
New class created:
class SUBGHZSPIClass : public SPIClass
By default debug pins are not configured, only internal SPI are configured.
To enable the debug pins simply call:
SubGHZ_SPI.enableDebugPins();
Then you can connect an analyzer to see signals. Example:

Send "Hello World!" in ascii (48 65 6c 6c 6f 20 57 6f 72 6c 64 21).
/cc @matthijskooijman @LMESTM