I am working on a embedded project trying to learn some of the ins and outs of programming an embedded board. As one might have guessed, this involves writing code in C. I am not having too much trouble getting the grasp of setting things up correctly (in terms of ports/pins/etc..) but I am looking to abstract some of my code to make it a bit more readable.
For example, the following code turns on a Green LED that is on the board:
// Required CPU Speed Define
#define F_CPU 16000000
// Include the necissary library header files
#include <avr/io.h>
int main() {
DDRD |= (1 << DDD5);
PORTD |= (1 << PD5);
PORTD ^= (1 << PD5);
for(;;) { }
}
On the other hand, I can #define the names of the ports so they are more clear, but that doesn't seem like the ideal solution (and it's unfortunately the method that I am currently using).
I would like to abstract some of the setting/functionality of enabling the on board leds (which I eventually hope to extend to other concepts such as timers/interrupts/etc..).
How do I use Struct/Pointers to properly abstract this?
I am currently trying the following method, but it is falling flat, and the LED is failing to turn on:
OnBoardLED.h
typedef struct {
unsigned int dataDirectionRegister;
unsigned int portNumber;
unsigned int pinNumber;
} OnBoardLED;
void setDataDirectionRegister(OnBoardLED* led, unsigned int DDR);
void setPortNumber(OnBoardLED* led, unsigned int port);
void setPinNumber(OnBoardLED* led, unsigned int pin);
void turnOn(OnBoardLED* led);
void turnOff(OnBoardLED* led);
and
main.c
#include "inc/OnBoardLED.h"
int main(void) {
OnBoardLED greenLED;
setDataDirectionRegister(&greenLED, DDRD);
setPortNumber(&greenLED, PORTD);
setPinNumber(&greenLED, 5);
turnOn(&greenLED);
for(;;) { }
}
I know that I should be using pointers in this instance, specifically for the Data Direction Registers and the Port (so that I am properly referencing that memory location), but I do not know how to properly reference them.
What am I missing here?
Note: If needed I will post my current implementations of each function, defined in OnBoardLED.c
Edit:
OnBoardLED.c
#include "inc/OnBoardLED.h"
void setDataDirectionRegister(OnBoardLED* led, unsigned int DDR) {
led->dataDirectionRegister = DDR;
}
void setPortNumber(OnBoardLED* led, unsigned int port) {
led->portNumber = port;
}
void setPinNumber(OnBoardLED* led, unsigned int pin) {
led->pinNumber = pin;
}
void turnOn(OnBoardLED* led) {
led->dataDirectionRegister |= (1 << led->pinNumber);
led->portNumber |= (1 << led->pinNumber);
led->portNumber ^= (1 << led->pinNumber);
}
I'm not too much practical with AVR toolchain but from what I remember and understand things like DDRD are macro which map addresses from address space to register of the CPU.
So by inspecting the source code you obtain something like:
#define __SFR_OFFSET 0
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define DDRD _SFR_IO8(0x0A)
This means that replacing DDRD yields something like:
*(volatile uint8_t*)((0x0A) + 0)
which is a smart macro, when used as a left-hand operand allows you to store a value at that memory address, when used as a right-hand operand you can read the value of the register. But this is still a macro and macros can hide evil details like in this situation.
What happens is that your code is
led->dataDirectionRegister = *(volatile uint8_t*)((0x0A) + 0);
led->dataDirectionRegister |= (1 << led->pinNumber);
so what you are doing is just saving the value of the data direction register into the struct member and then overwrite it with the value you would like to save into the register. So nothing basically happens.
What you need is to save the address of the data direction register to be able to dereference it later when calling the turnOn method. This is easily accomplished by having a member declared as
volatile uint8_t* dataDirectionRegister;
So that you can do
void setDataDirectionRegister(OnBoardLED* led, volatile uint8_t* DDR) {
led->dataDirectionRegister = DDR;
}
and invoke it as setDataDirectionRegister(&led, &DDRD);. Notice the & operator used to obtain the address of the memory location of the register.
But now you have an address so in your turnOn method you must dereference the variable to store data into it:
void turnOn(OnBoardLED* led) {
*led->dataDirectionRegister |= (1 << led->pinNumber);
...
So let's make a concrete example to show you the problem and how to fix it:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define __SFR_OFFSET 0
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(&buffer[0] + mem_addr))
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define DDRD _SFR_IO8(0x0A)
uint8_t buffer[256];
int main(void) {
memset(&buffer[0], 0, 256);
DDRD = 0x40;
printf("value: %x\n", (int)DDRD);
*(volatile uint8_t*)(&buffer[0] + (0x0A + 0)) = 0x41;
printf("value: %x\n", (int)DDRD);
// now let's do what you are doing in your struct
unsigned int dataDirectionRegister = DDRD; // value of register is copied into variable
dataDirectionRegister = 0x80; // variable is changed but not real register
printf("register: %x, variable: %x\n", (int)DDRD, dataDirectionRegister);
// you must save an address to do what you need
volatile uint8_t* realDataDirectionRegister = &DDRD;
*realDataDirectionRegister = 0x80;
printf("register: %x, variable: %x\n", (int)DDRD, (int)*realDataDirectionRegister);
return 0;
}
Mind that I used a buffer as my fake memory space, since you don't have a flat memory model free to use like in a x86 architecture like you have in a microcontroller.
Related
I just started learning about embedded systems and I'm having a bit of trouble with correctly setting up the led pin on my stm32f746ng-discovery board. I am not sure if I am not typecasting correctly or have the wrong address for the pin however, I do believe I have addressed everything correctly and I'm not seeing a value change for the GPIO output data register in the watch window which leads me to believe there might be an issue with my code.
To define the registers and their respective addresses this is the approach I took:
//Referring to STM32F746xx Memory Map and Register Boundary Addresses:
#define PERIPH_BASE (0x40000000UL)
#define AHB1PERIPH_OFFSET (0x00020000UL)
#define AHB1PERIPH_BASE (PERIPH_BASE + AHB1PERIPH_OFFSET)
#define GPIOI_OFFSET (0x2000UL)
#define GPIOI_BASE (AHB1PERIPH_BASE + GPIOI_OFFSET)
#define RCC_OFFSET (0x3800UL)
#define RCC_BASE (AHB1PERIPH_BASE + RCC_OFFSET)
#define RCC_AHB1EN_R_OFFSET (0x30UL)
#define RCC_AHB1EN_R (*(volatile unsigned int *)(RCC_BASE + RCC_AHB1EN_R_OFFSET)) //register
#define MODE_R_OFFSET (0x00UL)
#define GPIOI_MODE_R (*(volatile unsigned int *)(GPIOI_BASE + MODE_R_OFFSET)) //register
#define OD_R_OFFSET (0x14UL)
#define GPIOI_OD_R (*(volatile unsigned int *)(GPIOI_BASE + OD_R_OFFSET)) //register
#define GPIOIEN (1U << 0)
#define PIN_1 (1U << 1)
#define LED_PIN PIN_1
The above hex addresses I located from the stm32f746xx datasheet's memory map/table and RM0385 reference manual for the stm32f74xxx.
The code below is the main function where I try to change the bit value of the GPIOI_OD_R register:
int main(void)
{
/* 1. Enable clock access for GPIOI.*/
/* 1.1 I use the OR operator to only change the first bit instead of the whole 32bit chain. */
RCC_AHB1EN_R |= GPIOIEN;
/* 2. Sets PIN_1 as output.*/
GPIOI_MODE_R |= (1U << 2);
GPIOI_MODE_R &=~(1U << 3);
while(1)
{
/* 3. Sets PIN_1 high */
GPIOI_OD_R |= LED_PIN;
}
}
The problem that I am having is that the bit value for the GPIOI_OD_R register is not updating correctly and gets set to 00 instead of 01 which is the required value for the GPIOI Pin_1 (LED) to be set to the general-purpose output mode.
The above parameters I got from the RM0385 reference manual for the stm32f74xxx which can be seen in the image below:
However, when running the code the GPIOI_MODE_R and the GPIOI_OD_R bit values do not change which can be seen in the image below:
I need the values of the registers to be correct to set the LED PIN high on my stm32f746ng-discovery board.
I tried combining the GPIOI_MODE_R setting operation into a single one: GPIOI_MODE_R = (GPIOI_MODE_R | (1U << 2)) & ~(1U << 3) however that causes the program to loose connection with the debugger.
I am using the STM32CubeIDE with the following MCU GCC Compiler Settings:
Thanks in advance and if have referenced something incorrectly please excuse me im new to embedded systems.
I found the issue and realised that I was addressing the wrong bit value for the GPIOIEN. Instead of looking at the bit address for GPIOIEN with:#define GPIOIEN (1U << 8) I was making the mistake to look at the bit address for GPIOAEN with:#define GPIOIEN (1U << 0).
A super silly mistake from my side however I think it might be a mistake that many beginners like myself may make. The only advice that I can give from what I experienced and the process it took to solve the issue is to try and be extra accurate and properly focus when reading through the various board's references manuals and datasheets. When it comes to solving the issue I would say it is important to stay consistent with your code which makes it much easier during the debugging process because I followed this methodology I was able to trace each srep of my code and compare what I expected the values to be versus what I was actually getting.
The final code solution I have attached below:
//Referring to STM32F746xx Memory Map and Register Boundary Addresses:
#define PERIPH_BASE (0x40000000UL)
#define AHB1PERIPH_OFFSET (0x00020000UL)
#define AHB1PERIPH_BASE (PERIPH_BASE + AHB1PERIPH_OFFSET)
#define GPIOI_OFFSET (0x2000UL)
#define GPIOI_BASE (AHB1PERIPH_BASE + GPIOI_OFFSET)
#define RCC_OFFSET (0x3800UL)
#define RCC_BASE (AHB1PERIPH_BASE + RCC_OFFSET)
#define RCC_AHB1EN_R_OFFSET (0x30UL)
#define RCC_AHB1EN_R (*(volatile unsigned int *)(RCC_BASE + RCC_AHB1EN_R_OFFSET))
#define MODE_R_OFFSET (0x00UL)
#define GPIOI_MODE_R (*(volatile unsigned int *)(GPIOI_BASE + MODE_R_OFFSET))
#define OD_R_OFFSET (0x14UL)
#define GPIOI_OD_R (*(volatile unsigned int *)(GPIOI_BASE + OD_R_OFFSET))
#define GPIOIEN (1U << 8) // updated from (1U << 0)
#define PIN_1 (1U << 1)
#define LED_PIN PIN_1
int main(void)
{
/* 1. Enable clock access for GPIOI.*/
/* 1.1 I use the OR operator to only change the first bit instead of the whole 32bit chain. */
RCC_AHB1EN_R |= GPIOIEN;
/* 2. Sets PIN_1 as output.*/
GPIOI_MODE_R |= (1U << 2);
GPIOI_MODE_R &=~(1U << 3);
while(1)
{
/* 3. Sets PIN_1 high */
GPIOI_OD_R |= LED_PIN;
}
}
I've tried in C to make the WS2812 works in mplab with pic18f4550, the energy reach the led, but it doesn't turn on and I can't select the led that I ant to use, but it doesn't, how can I solve this? This is the code that I'm using:
Thanks
main.c
#define _XTAL_FREQ 4000000UL
#include "config.h"
#include "ws2812.h"
void main(void) {
ADCON1=0b00001111;
TRISB=0b00000001;
if (energy_port==1){
pin_strip_led=1;
ws2812_setPixelColorLed(1, ws2812_Color(255, 0, 0));
}
else{
pin_strip_led=0;
}
return;
}
This is what I'm using in ws2812.h:
#define STRIP_SIZE 8
#define pin_strip_led PORTBbits.RB1
#define energy_port PORTBbits.RB0
void ws2812_setPixelColorLed(unsigned char pixel, unsigned long color) {
Strip_RGBData[pixel][0] = (char) (color >> 16);
Strip_RGBData[pixel][1] = (char) (color >> 8);
Strip_RGBData[pixel][2] = (char) (color);
}
unsigned long ws2812_Color(unsigned char r, unsigned char g, unsigned char b) {
return ((unsigned long) r << 16) | ((unsigned long) g << 8) | b;
}
The program shown will read port B0, set B1 to the same state, and if set, populate one row of the array Strip_RGBData (which is neither declared nor used). It will do this one time. You ask, "How can I solve this?"
A good place to start is with the WS2812 datasheet. Use it to determine how to compose the signal required to control the LEDs. In your code set up a timer to clock out the bits of that signal in compliance with the timing requirements.
After that is working, you can add a loop to read the input to select an individual LED.
I am relatively new in programming µC in C and have previously always used the arduino IDE. I would like to create a function that sets and clears a pin. I tried this
#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 16000000UL
void set_led(int poort,int pin){
poort |= (1<<pin);
//PORTB |= (1<<pin); <-- this works
}
void clear_led(int poort,int pin){
poort &= ~(1<<pin);
}
int main(void)
{
DDRD = 0xff;
PORTD = 0x00;
while(1)
{
set_led(PORTD,PD7);
_delay_ms(500);
clear_led(PORTD,PD7);
_delay_ms(500);
}
}
The pin variable works like it should but when I implement the poort variable the led does not blink anymore. Does someone know how to fix this?
I use eclipse(AVR) on manjaro and the controller is an arduino nano.
Since C is pass by value only the local variable is changed.
You could either use macros:
#define SET_LED(POORT, PIN) ((POORT) |= (1<<(PIN)))
or pass the variable as pointer:
void set_led(volatile uint8_t *poort, int pin)
{
*poort |= (1<<pin);
}
and call it with set_led(&port, pin); for example.
The type int is probably wrong and should be volatile uint8_t.
So I am trying to write a function which takes a pointer to a struct, an element of the struct (representing a register) and an int which represents the bit in the register we want to set
My idea was
SET_GPIO_BIT(GPIO_TypeDef *PORT, volatile uint32_t reg, uint32_t bit) {
PORT->reg |= (1 << bit);
}
where my struct in my header file looks like
typedef struct {
volatile uint32_t MODER;
volatile uint32_t OTYPER;
//insert more registers here
volatile uint32_t BSRR;
} GPIO_TypeDef;
but then I get a (arm-none-eabi-gcc) compiler error:
error: 'GPIO_TypeDef {aka struct <anonymous>}' has no member named 'reg'
The basic idea is that I want to be able to write something like
SET_GPIO_BIT(GPIOA, BSRR, 7);
which will select the GPIO port A and set the seventh bit in the BSRR register (for example). Is there a way to do something like this?
Since what you're looking for is text substitution, you need a macro for this:
#define SET_GPIO_BIT(port, reg, bit) ((port)->reg |= (1 << (bit)))
Alternately, since all registers are of the same type, you could instead use a function and pass in the address of the register in question depending on the situation:
void SET_GPIO_BIT(volatile uint32_t *reg, uint32_t bit) {
*reg |= (1 << bit);
}
Then you would call it like this:
if (set_MODER) {
SET_GPIO_BIT(&GPIOA->MODER, 7);
} else if (set_OTYPER) {
SET_GPIO_BIT(&GPIOA->OTYPER, 7);
...
This is not an answer to the stated question; it is rather a suggestion to solve the underlying problem in a different way.
Rather than having the "registers" as separately named members in a structure, use an array instead:
enum {
IOREG_MODE = 0,
IOREG_OTYPE,
/* All registers, in order */
IOREG_COUNT
};
typedef struct {
volatile uint32_t ioreg[IOREG_COUNT];
} ioregs;
This lets you use either a macro,
#define IOREG_SET(port, reg, bit) (((port)->ioreg[reg]) |= (1u << (bit)))
#define IOREG_UNSET(port, reg, bit) (((port)->ioreg[reg]) &= ~(1u << (bit)))
#define IOREG_TOGGLE(port, reg, bit) (((port)->ioreg[reg]) ^= (1u << (bit)))
or a static inline function,
static inline uint32_t ioreg_set(regs *const port,
const int reg,
const unsigned char bit)
{
return port->ioreg[reg] |= 1u << bit;
}
static inline uint32_t ioreg_unset(regs *const port,
const int reg,
const unsigned char bit)
{
return port->ioreg[reg] &= ~(1u << bit);
}
static inline uint32_t ioreg_toggle(regs *const port,
const int reg,
const unsigned char bit)
{
return port->ioreg[reg] ^= 1u << bit;
}
On any modern C compiler, both are equally fast, but the static inline functions provide compile-time type checking, and make it easier to debug cases where wrong data is output (via, for example, duplicating the writes to a debug text stream).
(If your I/O registers supports also 8-bit and/or 16-bit accesses, you can make the structure an union of arrays, with different static inline accessors depending on the size of the register.)
I searched far and wide and information on the net seems to suggest that conditional compilation using the preprocessor works exclusively on environment variables.
Basically, I would like to have an inline function or macro perform different operations based in its input arguments. Example of what I want to achieve:
inline void foo(int x) {
#if (x < 32)
register0 |= (1 << x);
#else
register1 |= (1 << (x - 32));
#endif
}
The main goal here is that the resulting inline code will not contain conditional code for constant inputs.
Basically, I currently program for a microcontroller (lpc213x) and would like to have an inline function or macro to do pin configuration setup. Since pin configurations are split across multiple registers (e.g. 0 and 1 above), I would like to perform some conditional checks to decide which register is supposed to be written to for a given pin constant.
However, the pin configurations are all constant at compile time, so I would like to eliminate the conditional checks from compile code. I know that optimization would likely get rid of unnecessary conditionals anyway, but I'm wondering whether there is a way to achieve this behavior explicitly, because I might need to disable optimization in the future.
Thanks,
FRob
With C++, you could use template functions, like this:
template <bool B>
void doSetRegister(int x);
template<>
inline void doSetRegister<true>(int x) {
register0 |= (1 << x);
}
template<>
inline void doSetRegister<false>(int x) {
register1 |= (1 << (x - 32));
}
template <int X>
inline void setRegister() {
doSetRegister<X <= 32>(X);
}
int main() {
setRegister<1>();
setRegister<33>();
}
Here's an ugly way for C:
#include <stdio.h>
volatile unsigned long register0 = 0, register1 = 0;
#define DEF0X(_X_) \
static inline void SetPin##_X_() { register0 |= 1ul << _X_; }
#define DEF1X(_X_) \
static inline void SetPin##_X_() { register1 |= 1ul << (_X_ - 32); }
DEF0X(0) DEF0X(1) DEF0X(2) DEF0X(3) DEF0X(4)
DEF0X(5) DEF0X(6) DEF0X(7) DEF0X(8) DEF0X(9)
DEF0X(10) DEF0X(11) DEF0X(12) DEF0X(13) DEF0X(14)
DEF0X(15) DEF0X(16) DEF0X(17) DEF0X(18) DEF0X(19)
DEF0X(20) DEF0X(21) DEF0X(22) DEF0X(23) DEF0X(24)
DEF0X(25) DEF0X(26) DEF0X(27) DEF0X(28) DEF0X(29)
DEF0X(30) DEF0X(31) DEF1X(32) DEF1X(33) DEF1X(34)
DEF1X(35) DEF1X(36) DEF1X(37) DEF1X(38) DEF1X(39)
DEF1X(40) DEF1X(41) DEF1X(42) DEF1X(43) DEF1X(44)
DEF1X(45) DEF1X(46) DEF1X(47) DEF1X(48) DEF1X(49)
DEF1X(50) DEF1X(51) DEF1X(52) DEF1X(53) DEF1X(54)
DEF1X(55) DEF1X(56) DEF1X(57) DEF1X(58) DEF1X(59)
DEF1X(60) DEF1X(61) DEF1X(62) DEF1X(63)
#define SET_PIN(_X_) SetPin##_X_()
int main(void)
{
SET_PIN(0);
SET_PIN(1);
SET_PIN(32);
SET_PIN(63);
printf("register0=0x%08lX register1=0x%08lX\n",
register0, register1);
return 0;
}
Output:
register0=0x00000003 register1=0x80000001
And if you are coding in C, only thing you can do is to define two versions of foo().
You can remove conditional code by using short-circuit evaluation trick:
void foo(int x) {
x < 32 && (register0 |= (1 << x));
x >= 32 && (register1 |= (1 << (x - 32)));
}
try this
#define X 15 // or some else
#if X <= 32
# define SET_X register0 |= (1 << X)
#else
# define SET_X register1 |= (1 << (x - 32))
#endif
If you use the C Preprocessor to create a set of defines composed of two unsigned longs and then use these as arguments to your function foo(), the code is pretty straightforward. And if you need to add another register, you can modify the defines.
#define PIN_00 0x00000001, 0x00000000
#define PIN_01 0x00000002, 0x00000000
// defines left out here
#define PIN_31 0x80000000, 0x00000000
#define PIN_32 0x00000000, 0x00000001
// defines left out here
#define PIN_63 0x00000000, 0x80000000
inline void foo (unsigned long reg1, unsigned long reg2)
{
register0 |= reg1;
register1 |= reg2;
}
Then to use this function foo() you would specify the appropriate define. Since bitwise ORing with zero makes no change, the registers will be updated appropriately.
foo (PIN_00);
foo (PIN_01);
foo (PIN_32);
foo (PIN_63);
If you needed to add a way to remove bits you can make a new function using the same defines as follows:
inline void fooRemove (unsigned long reg1, unsigned long reg2)
{
register0 &= ~reg1;
register1 &= ~reg2;
}