Creating iterable list of register in embedded C - c

I have been programming with python java and c++, which all have list objects predefined. I'm now working on a microcontroller in C embedded, but objects such as lists and functions such as printf simply don't exist.
What I am trying to do is the following. I have multiple registers which I attach to defines. I want to put all my defines in a list I can access.
#include <stdlib.h>
#include <stdio.h>
#include <xc.h>
#define KP_ROW1 LATBbits.LATB0
#define KP_ROW2 LATBbits.LATB1
#define KP_ROW3 LATBbits.LATB2
#define KP_ROW4 LATBbits.LATB3
#define KP_COL1 LATBbits.LATB4
#define KP_COL2 LATBbits.LATB5
#define KP_COL3 LATBbits.LATB6
#define KP_COL4 LATBbits.LATB7
KP_ROW = [KP_ROW1, KP_ROW2, KP_ROW3, KP_ROW4]; //error on this line
KP_COL = [KP_COL1, KP_COL2, KP_COL3, KP_COL4]; //error
for(int i=0;i<4;i++)
{
if (KP_COL[i] == 1){return 1;}
}
Since I have no previous experience in C embedded, I assumed that google could help me, but it seems that all the solutions I find need the good understanding of struct and the fabrication of really complex functions.
The reasons for this is that I will have maybe 100 pins and I don,t want to make "if" statements for each, I want to iterate throughout a list. I'm using a PIC18F with the XC8 compiler.
I'm asking for advice. How would you do it? Is there a faster, simpler way than making your own list class?

It's quite hard to understand what is desired here. I understand that you wish to read the values of 100 register in your program. You have to know their addresses. Then, you can create a series of macros:
#define REG1 0xabcd
#define REG2 0x1234
...
or whichever addresses. Then, create more macros to access the values directly:
#define REG1_VAL (*(volatile uint8_t *) REG1)
#define REG2_VAL (*(volatile uint8_t *) REG2)
...
Then you can write assignments such as REG1_VAL = 1 or tests such as REG1_VAL == 1. Otherwise, you can create macros that access their values through their addresses:
#define READ(reg) (*(volatile uint8_t *) reg)
Usage of the above to obtain a register value is then:
READ(REG1);
You could then allocate an array of 100 elements:
volatile uint8_t regs[100] = {REG1, REG2, ..., REG100};
and iterate through that array:
for (i = 0; i < 100; i ++) {
if (READ(regs[i]) == 1) {
...
}
}
Hope this helps you!
Note: These macros would need fine tuning to avoid subtle errors, but I hope they are still useful to illustrate what I mean.

I found the answer!
It was not working because I was trying to get the adress of the bit instead of the register.... my bad. It should rather be:
#define KP_ROW LATB
And then add some bitmasks to write the pins I want.
Sorry for this beginner's error!

Related

Assigning #define macros to variable

I am trying to change some code but am running into issues. Here are the existing lines:
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL)
#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400UL)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define SSD1306_CS_Port GPIOF
I want to make SSD1306_CS_Port a variable and change the value in real time.
Thanks!
I tried:
GPIO_TypeDef *SSD1306_CS_Port = GPIOF;
But got an error about expansion of macro. What am I not getting here?
Your pointer can't have the same name as macro.
GPIO_TypeDef *SSD1306_CS_Port = GPIOF;
^^^^^ It is invalid
The name has to be distinct
GPIO_TypeDef *SSD1306_CS_Port_ptr = GPIOF;
You do not need to declare this pointer (it is even wrong as you add another level of indirection and use SRAM). Simple use:
SSD1306_CS_Port -> ODR |= SSD1306_CS_Pin;
//you need to define SSD1306_CS_Pin as well of course
//same with other GPIO registers

How to separate #defined arguments in C

I'm working on an embedded project and I have all the ports and pins defined like this:
#define SENSOR_1 gpioPortA,15
Is there a way to extract just the integer without creating a new #define?
So far I used a typedef and assigned the pin value from the #define like so:
typedef struct
{
GPIO_Port_TypeDef port;
uint8_t pin;
}sensor_t;
sensor_t sensor1 = {SENSOR_1};
/* Now sensor1.pin is the pin value */
but I want to pass the pin to a switch case and I can only use constant values for that and I'd rather avoid using an if statement.
Is there a way to pass the pin to a switch case without a new #define?
Short answer is no.
You could however use sensor1.pin when needed but not in case of switch statement as switch does not support variables there. In this case use if-else statement.
Or do it like this:
#define SENSOR_1_PIN 10
#define SENSOR_1 my_port,SENSOR_1_PIN
and then in switch you use SENSOR_1_PIN in case part.
switch (condition) {
case SENSOR_1_PIN:
//Do stuff
break;
//....
}
Just to remind again as it was posted in first comment on your question. Doing that is very dangerous.
Most proper way would be to do it like this:
#define SENSOR_1_PORT GPIOB
#define SENSOR_1_PIN 15
//If structure order changes here, you may lead to wrong data interpretation
sensor_t sensor1 = {SENSOR_1_PORT, SENSOR_1_PIN};
If you are C99 compliant, you may do it even more safer like this:
//If structure order changes here, your data are still properly assigned to fields
sensor_t sensor1 = {.port = SENSOR_1_PORT, .pin = SENSOR_1_PIN};
You can define a macro to extract the value from your definition.
#define GET_SECOND(x, y) y
#define PIN_VALUE(x) GET_SECOND(x)
switch (pin) {
case PIN_VALUE(SENSOR_1):
/* ... */
break;
}
PIN_VALUE must allow SENSOR_1 to be expanded via helper macro so that the second part can be extracted.
The proper solution at this point is to re-design the definitions into something that makes more sense, or alternatively create new constants.
As a last resort, if you are stuck with these macros, you can parse them in the following way:
#include <stdio.h>
#include <stdint.h>
typedef int GPIO_Port_TypeDef; // whatever type this happens to be
typedef struct
{
GPIO_Port_TypeDef port;
uint8_t pin;
}sensor_t;
#define GET_FIELD(field,...) (sensor_t){__VA_ARGS__}.field
#define SENSOR_1 gpioPortA,15
int main (void)
{
int gpioPortA = 1;
printf("%d %d", GET_FIELD(port, SENSOR_1), GET_FIELD(pin, SENSOR_1));
}
The type-generic version would be:
#define GET_FIELD(type, field, ...) (type){__VA_ARGS__}.field
...
printf("%d %d", GET_FIELD(sensor_t, port, SENSOR_1), GET_FIELD(sensor_t, pin, SENSOR_1));
This scales variably no matter how many fields there are. This is however not recommended practice. Macros in general, and variadic macros in particular, should be avoided.
As for how to use run-time variables in case - you can't. Use an if-else if statement instead.
What about inserting a define inside a define ? Instead of directly adding 15, you could make a define holding 15 and insert it elsewhere.
E.g:
#define SENSORVAL 15
#define SENSOR_1 gpioPortA,SENSORVAL
typedef struct
{
GPIO_Port_TypeDef port;
uint8_t pin;
}sensor_t;
sensor_t sensor1 = {SENSOR_1};
/* Now sensor1.pin is the pin value */

C code repetition with small variances by means of preprocessor macros

I'm currently working on some peripheral drivers for a family of microprocessors. I need to write code for devices that are very similar that in some cases only vary in the number of peripherals.
I would like to write a driver for the UART peripheral but in some devices of the family there's only one available and for others there are two. The driver for UART2 y the same as for UART1 but instead of writing to UART1FOO register I need to write to UART2FOO register. Knowing this I would like to write a piece of code where a macro is able to repeat a big chunk of code but replacing the number of the register reference.
The ideal solution would be something like:
// File: uartdriver.h
#if __device1__
#define PRESENT_UARTS 1
#else
#define PRESENT_UARTS 2
#endif
#for CURRENT_ITEM in MAGIC_MACRO_THAT_RETURNS_LIST(PRESENT_UARTS)
void uart#CURRENT_ITEM#_init();
#endfor
I know that this pseudo code does not exist but I have also seen people do magic with macros. I must use C code and not C++ and I'm aware that the use of macros may not be the most friendly way to write this but I don't like to repeat code with such small variations.
What would be the best solutions that mimic the pseudocode above? Any other hint or advice? Is there any other alternative to macros in this situation?
Thank you very much for your time.
I think some people are suggesting something along the lines of this. Note, this is pseudo code ie I just typed this in without testing it so it likely has some bugs etc.
typedef uint8_t uart_id;
int uart_init(uart_id id, uint16_t baudrate);
typedef struct uart_definitions {
uart_id id;
uint16_t baudrate;
....
} udefs;
#define MAX_UDEFS 2
static udefs u_config[MAX_UDEFS] = {
{0, 1000},
{1, 8192}
};
uart_init(uart_id id, uint16_t brate) {
assert(id <= MAX_UDEFS);
u_config[id].baudrate = brate;
.....
}

C macros: Conditional code based on parameter value?

Is there a cleaner/simpler way to do this?
The below works OK, but I think it's ugly - I'd like a solution that doesn't need a separate #define for every possible invalid value passed as "port".
#define _port_A_config_digital(mask) // do nothing; this port is always digital
#define _port_B_config_digital(mask) AD1PCFGSET = (mask)
#define _port_C_config_digital(mask)
#define _port_D_config_digital(mask)
#define _port_E_config_digital(mask)
#define _port_F_config_digital(mask)
#define _port_G_config_digital(mask)
#define _port_H_config_digital(mask)
#define _port_I_config_digital(mask)
#define _port_J_config_digital(mask)
#define _port_K_config_digital(mask)
#define ConfigDigitalBits(port, mask) _port_##port##_config_digital(mask)
If "port" is anything other than B, I want a null statement.
I'd like to get rid of all the #defines other than the one that does something.
I want to do this because on this MCU all ports other than B are always digital and there's nothing to be done.
But calling ConfigDigitalBits() ought to be a valid thing to do for any port.
You could do something like
#define CONFIG_DIGITAL_BITS(PORT, MASK) \
do { if (PORT == 'B') AD1PCFGSET = (MASK); } while (0)
and trust (or check by reading the assembly) your compiler to evaluate the if condition at compile-time. That is,
CONFIG_DIGITAL_BITS('B', 0x42);
would generate code for only
AD1PCFGSET = 0x42;
and
CONFIG_DIGITAL_BITS('A', 0x42);
would generate no code at all.
The proposed code above has the problem that it ignores errors. For example,
CONFIG_DIGITAL_BITS('Z', 0x42);
would happily compile although there is no port Z. You could assert on this but this will only catch the error at run-time.
Once you got to this, consider getting rid of the macro at whole and use an inline function instead that will also permit constant propagation.
inline void
config_digital_bits(const char port, const unsigned mask)
{
assert(port >= 'A' && port <= 'K');
if (port == 'B')
AD1PCFGSET = mask;
}

Best way to define offsets via C preprocessor

I would like to define a macro that will help me to auto generate offsets. Something like this:
#define MEM_OFFSET(name, size) ...
MEM_OFFSET(param1, 1);
MEM_OFFSET(param2, 2);
MEM_OFFSET(param3, 4);
MEM_OFFSET(param4, 1);
should generate the following code:
const int param1_offset = 0;
const int param2_offset = 1;
const int param3_offset = 3;
const int param4_offset = 7;
or
enum {
param1_offset = 0,
param2_offset = 1,
param3_offset = 3,
param4_offset = 7,
}
or even (not possible using C-preprocessor only for sure, but who knows ;)
#define param1_offset 0
#define param2_offset 1
#define param3_offset 3
#define param4_offset 7
Is it possible to do without running external awk/bash/... scripts?
I'm using Keil C51
It seems I've found a solution with enum:
#define MEM_OFFSET(name, size) \
name ## _offset, \
___tmp__ ## name = name ## _offset + size - 1, // allocate right bound offset and introduce a gap to force compiler to use next available offset
enum {
MEM_OFFSET(param1, 1)
MEM_OFFSET(param2, 2)
MEM_OFFSET(param3, 4)
MEM_OFFSET(param4, 1)
};
In the comments to your post you mention that you're managing an EEPROM memory map, so this answer relates to managing memory offsets rather than answering your specific question.
One way to manage EEPROM memory is with the use of a packed struct. ie, one where there is no space between each of the elements. The struct is never instantiated, it is only used for offset calculations.
typedef struct {
uint8_t param1;
#ifdef FEATURE_ENABLED
uint16_t param2;
#endif
uint8_t param3;
} __packed eeprom_memory_layout_t;
You could then use code like the following to determine the offset of each element as needed(untested). This uses the offsetof stddef macro.
uint16_t read_param3(void) {
uint8_t buf;
eeprom_memory_layout_t * ee;
/* eeprom_read(offset, size, buf) */
eeprom_read(offsetof(eeprom_memory_layout_t, param3), sizeof(ee->param3), &buf);
return buf;
}
Note that the struct is never instantiated. Using a struct like this makes it easy to see your memory map at a glance, and macros can easily be used to abstract away the calls to offsetof and sizeof during access.
If you want to create several structures based on some preprocessor declarations, you could do something like:
#define OFFSET_FOREACH(MODIFIER) \
MODIFIER(1) \
MODIFIER(2) \
MODIFIER(3) \
MODIFIER(4)
#define OFFSET_MODIFIER_ENUM(NUM) param##NUM##_offset,
enum
{
OFFSET_FOREACH(OFFSET_MODIFIER_ENUM)
};
The preprocessor would then produce the following code:
enum
{
param1_offset,
param2_offset,
param3_offset,
param4_offset,
}
I'm sure somebody will figure a nice preprocessor trick to compute the offset values with the sum of its predecessors :)
If you are doing this in C code, you have to keep in mind that const int declarations do not declare constants in C. To declare a named constant you have to use either enum or #define.
If you need int constants specifically, then enum will work well, although I the auto-generation part might be tricky in any case. Off the top of my head I can only come up with something as ugly as
#define MEM_OFFSET_BEGIN(name, size)\
enum {\
name##_OFFSET = 0,\
name##_SIZE__ = size,
#define MEM_OFFSET(name, size, prev_name)\
name##_OFFSET = prev_name##_OFFSET + prev_name##_SIZE__,\
name##_SIZE__ = size,
#define MEM_OFFSET_END()\
};
and then
MEM_OFFSET_BEGIN(param1, 1)
MEM_OFFSET(param2, 2, param1)
MEM_OFFSET(param3, 4, param2)
MEM_OFFSET(param4, 1, param3)
MEM_OFFSET_END()
Needless to say, the fact that it requires the next offset declaration to refer to the previous offset declaration by name defeats most of the purpose of this construct.
Try something like:
#define OFFSET(x) offsetof(struct {\
char param1[1], param2[2], param3[4], param4[1];\
},x)
Then you can use OFFSET(param1), etc. and it's even an integer constant expression.

Resources