Schnoogle
Posts: 41
Joined: Sun Feb 11, 2018 4:47 pm

I can't get hardware PWM to work on PI3

Wed Jul 11, 2018 11:29 pm

Hi there,

before I loos all my hair ;) I just try to find a possible answer here...
I'm running in baremetal and try to get the hardware PWM running.
I've checked how wiringPI and circle does implement the same and adopt is, but neither a LED nor the Osszi is giving any output on the pins where PWM should be active...

Here is what I did so far:

Code: Select all

#define GPIO_ADDRESS 0x00200000 // GPIO base address
#define PWM_ADDRESS  0x0020C000 // PWM settings base address
#define CLK_ADDRESS  0x00101000	// clock management base address

//several Hardware PWM register offset in words
#define PWM_CTL		0x00	// PWM Control
#define PWM_STA		0x01	// PWM Status
#define PWM_DMAC	0x02	// PWM DMA Config
#define PWM_RNG1	0x04	// PWM Channel1 Range
#define PWM_DAT1	0x05	// PWM Channel1 Data
#define PWM_FIF1	0x06	// PWM FIFO input
#define PWM_RNG2	0x08	// PWM Channel2 Range
#define PWM_DAT2	0x09	// PWM Channel2 Data

//the CLK registers
#define CLK_CTLPWM		0x28	// clock control register as word offset refer to: https://de.scribd.com/doc/127599939/BCM2835-Audio-clocks
#define CLK_DIVPWM		0x29    // clock divisor register
#define CLK_PASSWORD (0x5a << 24) // fixed value need to be written to configure the clock manager

#define PWM_RANGE 1024

uint32_t *volatile pwmAddr;
uint32_t *volatile clkAddr;

void test_pwm() {
        pwmAddr = (uint32_t *)(BMC2835_PERI_BASE + PWM_ADDRESS);
        clkAddr = (uint32_t *)(BMC2835_PERI_BASE + CLK_ADDRESS);

	gpio_pin pin1, pin2
	gpio_get_pin(GPIO12, GPIO_ALT0, &pin1); // this just sets the PIN to ALT_FUNC0
	gpio_get_pin(GPIO13, GPIO_ALT0, &pin2); // this just sets the PIN to ALT_FUNC0
	// now setup PWM
	*(pwmAddr+PWM_RNG1) = PWM_RANGE; // default pwm range
	*(pwmAddr+PWM_RNG2) = PWM_RANGE;
	
	// to setup PWM first disable to be able to change the clock
	*(pwmAddr+PWM_CTL) = 0; // stop PWM
	*(clkAddr+CLK_CTLPWM) = CLK_PASSWORD | 0x1 << 5; // STOP pwm clock
	t_usleep(110);
	// wait for the clock not being busy any longer
	while ( (*(clkAddr+CLK_CTLPWM) & 0x80) != 0) {
		t_usleep(1);
	}
	//set the divisor of the clock
	uint32_t divisor = 2;
	*(clkAddr+CLK_DIVPWM) = CLK_PASSWORD | (divisor << 12);
	*(clkAddr+CLK_CTLPWM) = CLK_PASSWORD |0x1; // set clock source
	t_usleep(10);
	*(clkAddr+CLK_CTLPWM) = *(clkAddr+CLK_CTLPWM) | CLK_PASSWORD | 0x1 << 4; // enable clock
	*(pwmAddr+PWM_CTL) = 1 << 0 | 1 << 8; // enable both PWM channels in PWM mode
	
	// now write data to PWM
	uint32_t value = 256;
	*(pwmAddr+PWM_DAT1) = PWM_RANGE - value;
	*(pwmAddr+PWM_DAT2) = value;
	
	while(1);
}
As the PWM is running in "standard" mode I would have expected the _-_-_- on my osszi on one of the GPIO pins or a LED being more lit on the one than the other PWM pin....

I can't find that I'm doing something obviously wrong or I just missed one ore even several additional "activation" flags that need to be set ...
From the register content the PWM clock is running (the enabled and the busy bits are set)

Any hint would be much appreciated....

BR
Schnoogle

bzt
Posts: 160
Joined: Sat Oct 14, 2017 9:57 pm

Re: I can't get hardware PWM to work on PI3

Sun Jul 29, 2018 10:27 am

I'm sorry I don't know the answer to your question, but since you haven't got any answers yet, I suppose a guess is better than nothing.
From the register content the PWM clock is running (the enabled and the busy bits are set)
Having a busy flag set in a pheriperal usually means further operations are blocked. Read the BCM doc looking for how to acknowledge a PWM clock tick and clear the busy flag. Again, just a guess. I haven't used PWM clock. I use PWM only to reset and shutdown the board.

Cheers,
bzt

Schnoogle
Posts: 41
Joined: Sun Feb 11, 2018 4:47 pm

Re: I can't get hardware PWM to work on PI3

Wed Aug 01, 2018 6:32 pm

Hi bzt,

thanks for trying to help me out with this issue. From the documents I've seen so far for the RPi clocks, the busy flag mean, that the clock is running and creates clock cycles based on the current setting. To change the settings you need to stop the clock, wait until the busy flag is clear (this is what my code is doing) and after changing the settings the clock should be activated and the busy flag should be set to indicate the clock is running... However, I've also tried to use the code as in the pigpio library that could be found here: https://github.com/joan2937/pigpio but unfortunately this also does not seem to work on m baremetal Pi :/

I might start implementing some basic software PWM to control my motor's but as this is eating up a whole core to run it in parallel I would rather like to get the pulses created right away from the hardware :)

Thanks again.
BR,
Schnoogle

Return to “Bare metal, Assembly language”

Who is online

Users browsing this forum: No registered users and 5 guests