A new and more modern way to access and modify registers

@shawn, are you working through the T4.x peripheral set? Can you summarize which you've implemented so far? The timer peripherals are really the ones where I've done any significant work at the register level.
 
@shawn, are you working through the T4.x peripheral set? Can you summarize which you've implemented so far? The timer peripherals are really the ones where I've done any significant work at the register level.
I've got most of 'em in there already, including TMR and PIT. Adding a few more now, including GPT, PWM, PXP, and I2S.
 
Everything is passed as a template parameter and also inlined, that's why it compiles down to something equivalent to using masks and shifts directly.

I believe my approach does allows different peripheral parts to have different bits. I'll throw in the SAI fields and commit that so you can see.
Hmm ... so if I want to write a method like
C++:
bool initSAI(SAI& mySAI, SAImode_t mode) // mode is I2S, TDM, S/PDIF etc.
{
    switch (mode)
    {
        case SAI_mode_I2S:
            mySAI.TCR1 = value for TCR1
            mySAI.TCR2 = value for TCR2
                ...
            break;
            
        case SAI_mode_TDM:
            mySAI.TCR1 = different value for TCR1
            mySAI.TCR2 = another value for TCR2
                ...
            break;
    }
}
I don't quite see how I do something like that with your header, resulting in efficient code. Yes, I could template it, but I'd expect to end up with two copies of the binary, differing only in the base address of the hardware ... unless the optimiser is fantasically good at its job, of course. I'd rather not rely on that.
 
Hmm ... so if I want to write a method like
C++:
bool initSAI(SAI& mySAI, SAImode_t mode) // mode is I2S, TDM, S/PDIF etc.
{
    switch (mode)
    {
        case SAI_mode_I2S:
            mySAI.TCR1 = value for TCR1
            mySAI.TCR2 = value for TCR2
                ...
            break;
           
        case SAI_mode_TDM:
            mySAI.TCR1 = different value for TCR1
            mySAI.TCR2 = another value for TCR2
                ...
            break;
    }
}
I don't quite see how I do something like that with your header, resulting in efficient code. Yes, I could template it, but I'd expect to end up with two copies of the binary, differing only in the base address of the hardware ... unless the optimiser is fantasically good at its job, of course. I'd rather not rely on that.
Oh, I see what you mean now. Let me explore that.
 
I don't quite see how I do something like that with your header, resulting in efficient code. Yes, I could template it, but I'd expect to end up with two copies of the binary, differing only in the base address of the hardware ... unless the optimiser is fantasically good at its job, of course. I'd rather not rely on that.
I'll just add a clarification: I'm designing it so that the user of the library doesn't need to use templates. More info about your SAI use case on the way...
 
@joepasquariello I just pushed GPT, so all the timers should be there (TMR, PIT, GPT).

@h4yn0nnym0u5e Here's how to implement that function using this API:
C++:
bool initSAI(I2S_Layout* mySAI, SAImode_t mode) // mode is I2S, TDM, S/PDIF etc.
{
    switch (mode)
    {
        case SAI_mode_I2S:
            mySAI->TCR1 = value for TCR1
            mySAI->TCR2 = value for TCR2
                ...
            break;
           
        case SAI_mode_TDM:
            mySAI->TCR1 = different value for TCR1
            mySAI->TCR2 = another value for TCR2
                ...
            break;
    }
}

// Configure specific groups
initSAI(&I2S1::group, my_mode1);
initSAI(&I2S2::group, my_mode2);

Or, if you want to use references:
C++:
bool initSAI(I2S_Layout& mySAI, SAImode_t mode) // mode is I2S, TDM, S/PDIF etc.
{
    switch (mode)
    {
        case SAI_mode_I2S:
            mySAI.TCR1 = value for TCR1
            mySAI.TCR2 = value for TCR2
                ...
            break;
           
        case SAI_mode_TDM:
            mySAI.TCR1 = different value for TCR1
            mySAI.TCR2 = another value for TCR2
                ...
            break;
    }
}

// Configure specific groups
initSAI(*I2S1::group, my_mode1);
initSAI(*I2S2::group, my_mode2);

[I just love it in C++ when the address and dereference operators are opposites when passing as parameters...]
 
Last edited:
Note: I just updated I2S (remove non-existent fields from SAI2 and SAI3) and main.cpp (small ENET register use improvements).
 
I added a way to guarantee certain bits always get set to 1 with the new `AssignSet` parameter. Some registers in ENET and CCM need this.
 
Back
Top