Passing a Port as a variable - AVR - c++

Is it possible to use an AVR port as a variable which can be passed around?
For example
LED myLed(PORTA,7); //myLED hooked to PORTA, Pin 7
I would like to make LED be able to take any PORT / Pin combination, so I would rather not hard code it in.
Note that the PORTs are defined as:
#define PINA _SFR_IO8(0x00)
#define DDRA _SFR_IO8(0x01)
#define PORTA _SFR_IO8(0x02)
PORTA symbol resolves to (*(volatile uint8_t *)((0x02) + 0x20))
I believe this would allow me to do something like the following, but I am unsure whether I would need the volatile keyword or not, nor whether it will actually work as expected
class LED{
public:
LED(volatile uint8_t* port, uint8_t pin);
{
Port=port;
Pin=pin;
}
void write(bool val)
{
if(val) (*Port) |= 1 << Pin;
else (*Port) &= ~(1 << Pin);
}
private:
uint8_t Pin
volatile uint8_t* Port;
}
Finally, is there a way to set Port / Pin as an Output from the LED Constructor?
This would involve finding the relative DDR# register for the Given PORT#.
Can I assume &DDR# will always be &PORT#-1?

The register macros are basically pointers to the memory location, where the appropriate register resides, so yes, you can use uint8_t volatile *. However, the compiler will not generate the most efficient code this way -- it will use indirect addressing instead of direct writes.
This is what I do instead, using avrlib.
#include <avrlib/porta.hpp>
#include <avrlib/pin.hpp>
using namespace avrlib;
typedef pin<porta, 4> led_pin;
Then you can use the led_pin typedef, e.g.
led_pin::set();

This is how I did it, im not very experienced in AVR,
#include <avr/io.h>
void LED(volatile uint8_t* port, uint8_t pin)
{
// First set DDRx ; DDRx on ATmega32 is one address below port address
*(port -1) |= (1<< pin);
// Now set the pin high
*port |= (1<< pin);
}
int main(void)
{
LED(&PORTB,1);
LED(&PORTC,2);
LED(&PORTD,3);
while (1)
{
}
}

Ports are nothing more than I/O addresses, so all you have to do is pass the address of the I/O port to you LED constructor:
LED *light = new LED(&PORTA, 4);
Why does this work? PORTA resolves, as you already mentioned, to the dereferencing of a pointer:
(*(volatile uint8_t *)((0x02) + 0x20))
So adding an address of operator in front creates
&(*(volatile uint8_t *)((0x02) + 0x20))
which can be simplified to
(volatile uint8_t *)((0x02) + 0x20)

Related

Structure pointer initialized with a value behaves like enum

I was reading an article regarding bare bones programming of ARM processor when I came across the following code
struct systick {
volatile uint32_t CTRL, LOAD, VAL, CALIB;
};
#define SYSTICK ((struct systick *) 0xe000e010)
#define BIT(x) (1UL << (x))
static inline void systick_init(uint32_t ticks) {
if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit
SYSTICK->LOAD = ticks - 1;
SYSTICK->VAL = 0;
SYSTICK->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick
RCC->APB2ENR |= BIT(14); // Enable SYSCFG
}
I am not able to understand how does initializing a pointer to a structure initialize variables inside a structure? Does the ARM compiler treat it as an enum?
Thanks!
It's very likely a memory-mapped device. So, when you write to structure members, you're essentially manipulating the hardware, just as with port output (but in this case, memory-mapped). So you're essentially writing to a hardware that's available on addresses 0xe000e010, 0xe000e014, 0xe000e018, 0xe000e01c respectively for CTRL, LOAD, VAL, CALIB. Then you set particular bits manually.

How to format data to be in type const uint16_t for use in an spi library

so I have been writing some code and finished it, all I had left to do was send it over SPI. However, I didn't realise that the SPI library I am using only accepts const uint_16t data. I tried setting up a temp variable of the correct data type and using a reference to send over SPI, which works, but because it is a const variable it cant change, which is something I need it to be able to do.
Below is my code, any help with this would be great, for reference this is done in C++ and uses SPI library is part of the Raspberry Pi Pico SDK (the code is cut down as is long so only needed parts included). I did try to use pointers to change the const variable which I think you can do in C but I couldn't get it to work in C++.
Any help with this would be greatly appreciated as I am very stuck as with how to go about fixing this.
Thanks,
Dean
class Frequency_Values{
Public:
static uint16_t position;
//constructors and special member functions here
private:
uint16_t MSB_LUT[401];
uint16_t LSB_LUT[401];
};
//----------static variable definition---------------------
unsigned short Frequency_Values::position = 0;
//---------------------------------------------------------
//-------------------get function definitions--------------
uint16_t Frequency_Values::get_MSB_LUT_Value()
{
return (Frequency_Values::MSB_LUT[position]);
}
uint16_t Frequency_Values::get_LSB_LUT_Value()
{
return (Frequency_Values::LSB_LUT[position]);
}
//-----------------------------------------------------------
Frequency_Values MSB, LSB;
int main(){
while(1){ // to show code runs multiple times
//--------------------------------------------------
const uint16_t LSB_Holder = LSB.get_LSB_LUT_Value(); // this is what I tried to get it in the correct data type
//which worked but as const the value wont change
const uint16_t MSB_Holder = MSB.get_MSB_LUT_Value();
//--------------------------------------------------
}
spi_write16_blocking(SPI_PORT, &LSB_Holder, 1);
spi_write16_blocking(SPI_PORT, &MSB_Holder, 1);
}
This was eventually fixed by just removing the const specifier inside of the pico sdk, spi library.

How to pass Serial object as a number (New library using arduino Serial)

I want to create a new library which controls arduino's Serial library.
Here is what I want ...
main Sketch
#include <newLibrary.h>
newLibrary connection(2,9600); // 2 is Serial2, 9600 is baudRate
void main()
{
connection.start();
}
newLibrary.cpp
newLibrary::newLibrary (uint8_t port, long baudRate)
{
__port = port;
__baudRate = baudRate;
}
void newLibrary::start()
{
(Serial+port).begin(); // I need to add port to Serial to make Serial2
}
What I want to do is,
The user will choose which Serial port(eg. Serial/Serial1/Serial2 etc...) is going to be used with ;
newLibrary connection(2,9600); // 2 is Serial2, 9600 is baudRate
and after that the start function in newLibrary.h will start that Serial port with an algorithm like;
void newLibrary::start()
{
(Serial+port).begin(); // Which is equal to Serial.begin() or Serial1.begin() etc
}
I know it can be done by if statement or switch case...
But is there another way?
Such as macros....
I know that the macros can be used like ;
#define serialPort (Serial##1) // so the serialPort refers to Serial1
But this way doesnt work for me....
C++ doesn't support reflection. You can't build variable names at runtime.
You could store pointers to the objects in a container.
#include <array>
class S {
public:
void begin() {}
} Serial, Serial1, Serial2;
int main() {
std::array serials = {&Serial, &Serial1, &Serial2};
std::uint8_t port = 1;
serials[port]->begin();
}
Instead of taking a uint8_t why don't you take a Stream object and then you can pass Serial2 directly like: newLibrary connection(Serial2,9600);
newLibrary::newLibrary (Stream port, long baudRate)
{
__port = port;
__baudRate = baudRate;
}
newLibrary connection(Serial2,9600);
void newLibrary::start()
{
_port.begin(_baudRate); // Which is equal to Serial.begin() or Serial1.begin() etc
}
Of course you also need to change the line in the class definition that defines _port, but since you didn't post that bit I'll assume you know how to do that.
Moral of the story is, those serial ports are objects of type Stream, so you can pass them around just like variables as long as you use the type Stream instead of int or byte or char.

Interrupt Service Routine can't be called directly

I am trying to code an Interrupt Handler in C++ without the use of Assembly through __attribute__((interrupt)).
Following code:
init/idt.cpp:
void set(IDTDescr *descriptor, void(*handler)(void), uint8_t flags)
{
descriptor->selector = 0x08;
uintptr_t handler_addr = (uintptr_t)handler;
descriptor->offset_1 = handler_addr & 0xFFFF;
descriptor->offset_2 = (handler_addr >> 16) & 0xFFFF;
descriptor->offset_3 = (handler_addr >> 32) & 0xFFFFFFFF;
descriptor->flags = flags;
}
void init(void)
{
memset(descriptors, 0, sizeof(descriptors));
//Faults:
set(&descriptors[0x00], &DivideByZero, IDT_PRESENT | IDT_INTERRUPT);
set(&descriptors[0x01], &Debug, IDT_PRESENT | IDT_INTERRUPT);
and so on.
interrupt/handler/stubs.h:
//Faults:
void DivideByZero(struct cpu_state *state);
void Debug(struct cpu_state *state);
and so on.
interrupt/handler/fault_irs.cpp:
__attribute__((interrupt)) void DivideByZero(struct cpu_state *state)
{
Text::Clear();
Text::Simple::Write("FAULT!");
abort();
}
__attribute__((interrupt)) void Debug(struct cpu_state *state)
{
Text::Clear();
Text::Simple::Write("FAULT!");
abort();
}
and so on.
The problem is that GCC tells me that i can't call a ISR directly. But i don't call it directly, do I? Do I have to define __attribute__((interrupt)) in void set too?
Would it maybe help if i would set the arguments in stubs.h to void?
The funny thing is that the first fault is accepted by gcc...
It seams like gcc wants me to put all isr's in a seperate file... If i do the same in interrupt/handler/irq_irs.cpp it works....
Maybe an additional explanation why i don't want to simply use a single Interrupt Handler: If i use a single IH, i'll have to use a switch-case to call the right function for handling the interrupt (because i can't put all of the handling code in the handler i'll have to use seperate functions). That's much overhead and doesn't really make things simpler... Even if i really have to create a file for 256 interrupts...

Read-only memory-mapped registers defined with `volatile const` in C but only `volatile` in C++

While working on an embedded systems project using an Atmel SAM3X8E, I noticed the following bit of code in some of the CMSIS header files.
#ifndef __cplusplus
typedef volatile const uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#else
typedef volatile uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#endif
Why does the typedef for C++ not include const? I saw somewhere a mention that C++ does not store integer const variables in runtime memory, which if true would mean the const would need to be removed because of how microcontroller registers are memory-mapped, but I can't seem to find anything else saying that C++ does that (though my search was admittedly pretty brief). Not having much experience with C++, I also thought it might be that C++ doesn't allow const struct members, as those typedefs are mostly used in struct typedefs for collections of registers, but that doesn't seem to be the case either.
If you declare with const, C++ standard will obligate you to initialize the contents of the variable. In the case of micro-controller register, you do not want to do that.
Because no RoReg object is ever instantiated, there is no good reason to omit the const qualifier in the typedef.
Every use of RoReg is in either a macro that defines a pointer to the type...
#define REG_WDT_SR (*(RoReg*)0x400E1A58U) /**< \brief (WDT) Status Register */
...or a struct declaration that is accessed using a similar macro.
typedef struct {
WoReg WDT_CR; /**< \brief (Wdt Offset: 0x00) Control Register */
RwReg WDT_MR; /**< \brief (Wdt Offset: 0x04) Mode Register */
RoReg WDT_SR; /**< \brief (Wdt Offset: 0x08) Status Register */
} Wdt;
#define WDT ((Wdt *)0x400E1A50U) /**< \brief (WDT) Base Address */
Even with the const qualifier, the code should behave the same in both C and C++.
Perhaps the author misinterpreted the standard. To guarantee that a C++ struct has the same layout as in C, it requires that the class "has the same access control (Clause 11) for all non-static data members." The author may have mistaken const and volatile for access control specifiers. If they were, then you would want all the struct members to have the same cv-qualifiers in order to ensure compatibility between the C and C++ (and hardware) layouts. But it's public, protected, and private that define access control.
As mentioned by #fanl, const does indeed change the default linkage of globals in C++, and does prevent defining a variable without initialization.
But there are better ways to get external linkage than removing const. The usage of reserved arrays in the header file Chris linked is also very fragile. I would say this code leaves a lot of room for improvement -- don't emulate it.
And furthermore these variables don't get defined (that would cause the compiler and linker to select an address), they are always accessed via pointers, with the address fixed according to the memory map.
For headers intended purely for use by C++, this is how I do it (memory map matching a TI Stellaris chip).
Looks complicated, but the optimizing compiler reduces it down to a single instruction per access. And the address offsets are coded in, not dependent on the order and padding of fields inside a structure, so it's much less fragile and easier to verify against the datasheet.
template<uintptr_t extent>
struct memory_mapped_peripheral
{
char data[extent];
volatile uint32_t* offset( uintptr_t off ) { return reinterpret_cast<volatile uint32_t*>(data+off); }
volatile const uint32_t* offset( uintptr_t off ) const { return reinterpret_cast<volatile const uint32_t*>(data+off); }
};
struct LM3S_SYSTICK : private memory_mapped_peripheral<0x1000>
{
volatile uint32_t& CTRL (void) { return offset(0x010)[0]; }
volatile uint32_t& RELOAD (void) { return offset(0x014)[0]; }
volatile uint32_t& CURRENT(void) { return offset(0x018)[0]; }
}* const SYSTICK = reinterpret_cast<LM3S_SYSTICK*>(0xE000E000);
struct LM3S_NVIC : private memory_mapped_peripheral<0x1000>
{
volatile uint32_t& EN (uintptr_t i) { return offset(0x100)[i]; }
volatile uint32_t& DIS (uintptr_t i) { return offset(0x180)[i]; }
volatile uint32_t& PEND (uintptr_t i) { return offset(0x200)[i]; }
volatile uint32_t& UNPEND(uintptr_t i) { return offset(0x280)[i]; }
volatile const uint32_t& ACTIVE(uintptr_t i) const { return offset(0x300)[i]; }
volatile uint32_t& PRI (uintptr_t i) { return offset(0x400)[i]; }
volatile uint32_t& SWTRIG(void) { return offset(0xF00)[0]; }
}* const NVIC = reinterpret_cast<LM3S_NVIC*>(0xE000E000);