Programming from scratch

Thread Starter

gogo00

Joined Oct 28, 2023
37
I'm not certain if my question makes complete sense, but I have a curiosity regarding program. assume we have datasheets of microcontroller contain information about various ports like Port 1, Port 2, Port 3, and Port 4, along with their respective memory addresses. 0x01, 0x02, 0x03 and 0x04

Let's assume we have a compiler installed on our system, and the task at hand is to write a small C program that sets the 7th bit of Port 1. Now, here's the twist: let's assume this is a new microcontroller developed , and we're writing code for it for the first time.

How should I go about writing this C program entirely from scratch by examining the datasheet to accomplish this task?

I want to clarify that this isn't a homework assignment or a time-bound task assigned by someone; it's a personal curiosity aand thought I'm sharing with you
 

MrChips

Joined Oct 2, 2009
30,488
Let us say that you bought a new microcontroller development kit.
You have the software development suite installed on your PC.
The first task is to make an LED on the kit turn ON.

(Ask questions if you do not understand any of the above.)
 

BobTPH

Joined Jun 5, 2013
8,664
How should I go about writing this C program entirely from scratch by examining the datasheet to accomplish this task?
You will need to create a header file defining the registers that you will need.

I have actually done this, since I developed a language and complier for PIC24. It does not use headers, it uses classes. So there is a parent class called PIC24 that contains the definitions shared by all processors in the family, and a derived class for each specific processor model that contains definitions that vary from one processor to the next. This is still a work in progress. I add new things as I need them.
 

Thread Starter

gogo00

Joined Oct 28, 2023
37
You will need to create a header file defining the registers that you will need
Thank you for your interest. Let's assume I have a header file with the following definitions

Code:
#define PORT1_ADDRESS 0x01 // Actual memory address from the datasheet

#define PORT1_7TH_BIT (1 << 7) // 7th bit mask
Now, the question is, how can we set the 7th bit of Port 1 from the main function in our C program?
 

dl324

Joined Mar 30, 2015
16,688
Now, the question is, how can we set the 7th bit of Port 1 from the main function in our C program?
Just OR it with the PORT.

The microcontroller you're using may provide macros for you to do that (e.g. GPIO_SET), or a pointer to the memory mapped address of the register (e.g. *PE_Data |= 0x3F8; )
 

MrChips

Joined Oct 2, 2009
30,488
For your first test, don't worry about just the one bit of any port.
Turn all bits on by setting the port to 0xFF.
 

BobTPH

Joined Jun 5, 2013
8,664
Those definitions do not help you. You need to define a variable called PORTA that is at the absolute location of 1. That cannot be done in standard C. How you do it depends on what extensions the complier you are using implements.

Here is how it is done in MIcrochip XC16:

extern volatile uint16_t PORTA __attribute__((__sfr__));

The the name PORTA is defined in a linker script:

PORTA = 0x2C2;
 
Last edited:

nsaspook

Joined Aug 27, 2009
12,796
With standard C you can use absolute addresses so it's possible to bit-bang any addressable memory space or register to the bit level, but it's a lot more fun with symbolic headers and linker files.
 

BobTPH

Joined Jun 5, 2013
8,664
With standard C you can use absolute addresses so it's possible to bit-bang any addressable memory space or register to the bit level, but it's a lot more fun with symbolic headers and linker files.
How do you use absolute addresses with standard C?
 

nsaspook

Joined Aug 27, 2009
12,796
Last edited:

nsaspook

Joined Aug 27, 2009
12,796
There is a more structured way to use absolute addressing.

On the RPi to have direct access to gpio and timer config registers we could do something like this if we have the datasheet to the various devices with address and register maps
C:
#define PERI_BASE   0x3F000000
#define BCM2708_PERI_BASE        PERI_BASE

#define ST_BASE                  (BCM2708_PERI_BASE + 0x3000)
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO */

    dev->iobase = GPIO_BASE; /* bcm iobase */
    /*
     * dev->mmio is a void pointer with 8bit pointer indexing,
     * we need 32bit indexing so mmio is casted to a (__iomem uint32_t*)
     * pointer for GPIO R/W operations
     */
    dev->mmio = ioremap(dev->iobase, SZ_16K);
    if (!dev->mmio) {
        dev_err(dev->class_dev, "invalid gpio io base address!\n");
        return -EINVAL;
    }

    devpriv->timer_1mhz = ioremap(ST_BASE, 8);
    if (!devpriv->timer_1mhz) {
        dev_err(dev->class_dev, "invalid 1MHz timer base address!\n");
        return -EINVAL;
    }

/*
* pinMode:
*      Sets the mode of a pin to be input, output
************************************************************
*/

static void pinModeGpio(struct comedi_device *dev,
    int32_t pin,
    int32_t mode)
{
    int32_t fSel, shift;

    pin &= 63;
    fSel = gpioToGPFSEL [pin];
    shift = gpioToShift [pin];

    if (mode == INPUT) /* Sets bits to zero = input */
        iowrite32(ioread32((__iomem uint32_t*) dev->mmio + fSel)
        & ~(7 << shift),
        (__iomem uint32_t*) dev->mmio + fSel);
    else
        if (mode == OUTPUT)
        iowrite32((ioread32((__iomem uint32_t*) dev->mmio + fSel)
        & ~(7 << shift)) | (1 << shift),
        (__iomem uint32_t*) dev->mmio + fSel);
}
 

WBahn

Joined Mar 31, 2012
29,865
I'm not certain if my question makes complete sense, but I have a curiosity regarding program. assume we have datasheets of microcontroller contain information about various ports like Port 1, Port 2, Port 3, and Port 4, along with their respective memory addresses. 0x01, 0x02, 0x03 and 0x04

Let's assume we have a compiler installed on our system, and the task at hand is to write a small C program that sets the 7th bit of Port 1. Now, here's the twist: let's assume this is a new microcontroller developed , and we're writing code for it for the first time.

How should I go about writing this C program entirely from scratch by examining the datasheet to accomplish this task?

I want to clarify that this isn't a homework assignment or a time-bound task assigned by someone; it's a personal curiosity aand thought I'm sharing with you
First and foremost, you need a compiler for THAT microcontroller's ISA (Instruction Set Architecture). It's not enough to just have some C compiler already installed. Without that, you are pretty much hosed until you write one.
 

owl_

Joined Nov 7, 2023
3
Can't you just assign the memory address to a pointer? That is if you are doing bare metal programming, using the bare metal compiler.
Let's say the GPIO memory map starts at address 0x40020000 and is one byte long. Each of the six first bits correspond to a pin and the last two are padding.
Let's say we want to set the sixth bit.

Code:
//use a pointer to the address
unsigned char * p = ( unsigned char *)0x40020000;
//set the 6th bit to high
*p |=  (1<< 5);
If on the other hand you are running inside an OS, let's say linux, then your program will be running inside a virtual address space and you must first use mmap (https://linux.die.net/man/2/mmap) in order to map the real memory address inside the virtual address space.

For an example of using mmap() along with the above bit setting process on a Pi check:
https://forums.raspberrypi.com/viewtopic.php?t=319170
A working example is given both in C and Assembly.
 

nsaspook

Joined Aug 27, 2009
12,796
Can't you just assign the memory address to a pointer? That is if you are doing bare metal programming, using the bare metal compiler.
Let's say the GPIO memory map starts at address 0x40020000 and is one byte long. Each of the six first bits correspond to a pin and the last two are padding.
Let's say we want to set the sixth bit.

Code:
//use a pointer to the address
unsigned char * p = ( unsigned char *)0x40020000;
//set the 6th bit to high
*p |=  (1<< 5);
If on the other hand you are running inside an OS, let's say linux, then your program will be running inside a virtual address space and you must first use mmap (https://linux.die.net/man/2/mmap) in order to map the real memory address inside the virtual address space.

For an example of using mmap() along with the above bit setting process on a Pi check:
https://forums.raspberrypi.com/viewtopic.php?t=319170
A working example is given both in C and Assembly.
Sure, that's what I did above with the RPi ioremap example: https://forum.allaboutcircuits.com/threads/programming-from-scratch.197072/post-1863089
https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch08s04.html
 

owl_

Joined Nov 7, 2023
3
Sure, that's what I did above with the RPi ioremap example:
Yeah, and your solution is more general since you are using kernel system calls that are chip agnostic.
I thought it might be simpler to understand with a real world example where we can use more direct memory addressing (in the baremetal case) or have the kernel worry about it (in the OS case).
 
Last edited:

nsaspook

Joined Aug 27, 2009
12,796
Yeah, and your solution is more general since you are using kernel system calls that are chip agnostic.
I thought it might be simpler to understand with a real world example where we can use more direct memory addressing (in the baremetal case) or have the kernel worry about it (in the OS case).
Either way, low-level absolute address can get tedious and is error-prone because layouts are hand calculated.
1699404307951.png

A bare-metal example for a section of DMA memory in a controller with memory caching (DMA needs uncached memory) and two separate memory bank connections to the processor. This allows for less contention of DMA and CPU memory accesses when updating the GLCD display in background. XC32 for the PIC32 series.
C:
#define    BANK1        0xA000A000    // bank 1 frame buffer memory address
#define    BANK2        0x80030000    // bank 2 frame buffer memory address

/* This array is the offscreen frame buffer used for rendering.
** It isn't possible to read back frome the OLED display device,
** so display data is rendered into this offscreen buffer and then
** copied to the display.
*  must be in uncached memory for pic32MZ DMA so use __attribute__((coherent))
* DMA0 SPI TX transfers DATA
* DMA2 SPI TX transfers CMD
* DMA1 GLCD buffer transfers
*/
#ifdef __32MK1024MCM100_H    // bank 2 for this CPU
uint8_t __attribute__((address(BANK2), coherent)) rgbOledBmp0[cbOledDispMax]; // two display buffers for page flipping
uint8_t __attribute__((address(BANK2 + cbOledDispMax), coherent)) rgbOledBmp1[cbOledDispMax];
#ifdef USE_DMA
static uint8_t __attribute__((address(BANK2 - 8), coherent)) rgbOledBmp_blank[4] = {0x00, 0x00, 0x00, 0x00}; // 32-bit frame-buffer clearing variable
#endif
volatile uint8_t __attribute__((address(BANK2 - 16), coherent)) rgbOledBmp_page[5];
#endif
Same section of code for a different processor with only one bank of memory so DMA memory bandwidth is shared with CPU access, slowing things down.
C:
#ifdef __32MK0512MCJ048__    // NO bank 2 for this CPU so memory is in bank 1
uint8_t __attribute__((address(BANK1), coherent)) rgbOledBmp0[cbOledDispMax]; // two display buffers for page flipping
uint8_t __attribute__((address(BANK1 + cbOledDispMax), coherent)) rgbOledBmp1[cbOledDispMax];
#ifdef USE_DMA
static uint8_t __attribute__((address(BANK1 - 8), coherent)) rgbOledBmp_blank[4] = {0x00, 0x00, 0x00, 0x00}; // 32-bit frame-buffer clearing variable
#endif
volatile uint8_t __attribute__((address(BANK1 - 16), coherent)) rgbOledBmp_page[5];
#endif
 
Last edited:

owl_

Joined Nov 7, 2023
3
it is implementation defined.
You mean that whether you are allowed to directly edit the memory address that you have loaded into the pointer or not differs from system to system. Not to mention how to get the correct address.
Yeah, but still, for something like a raspberry pi, it is as simple as that. And I'd think that this is the simpler way to explain it.
 

BobTPH

Joined Jun 5, 2013
8,664
You mean that whether you are allowed to directly edit the memory address that you have loaded into the pointer or not differs from system to system.
No, I mean if you load 0x1000 into a pointer there is no guarantee what address is accessed when you dereference it.

For example on 16-bit x86 architecture it would likely access 0x1000 from what is in the DS (data segment) register.
 
Top