I'm trying to code on the emulated ESP32 using C and memory registers directly. I know there are great SDKs but I'd like to learn first what is going on under the hood, so i decided to try simple tasks with this approach.
This is what I'm using as base: Tech ref.
This is the first time I'm trying C, my goal is not to have the perfect code, I want to understand how things work.
What I was able to do: I was able to use simple output to turn on and off the led, and I was also able to control them using a button. To do this I used the simple LEDs in wokwi emulator without any resistor, I tried with the LEDC module with the same setup.
Where I had problems: I got stuck trying to use the LEDC module to turn (half) on and off an led. I configured the timer, the channel and gpio to use the specific channel but nothing happened (GPIO pin 26 with peripheral 71 selected; hPoint of the duty cicle at 0 and duty 'length' of 120; the timer used the same config of the code below).
Since it was not working: I tried a different strategy trying to print the timer (aka counter) value but it is always 0, i expected to see it increasing and when reached the overflow reset and start again its cycle.
So I'm seeking to understand why the timer always returns 0.
I leave here the code of my last attempt.
To help those who want to try:
The timer is set with resolution of 8 bit (range 0-256 if i am not wrong); divisor of 1 (it starts on bit 5 but has 8 fractional bits). I tried also higher divisor to change the frequency but was not able to print the timer value (or to make the LED turning half on in previous attempts). I enabled the REF_TICK but this was just a test since with 0 in that bit it did not worked.
My goal is to understand what I'm missing (or messing) and be able to get the timer value; after that I'll go further and set the channel and the GPIO pin.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define GPIO_CFG_BASE 0x3FF44530;
#define LEDC_CONF_REG 0x3FF59190; //bit 1 to choose clk
#define LEDC_HSCH0_CONF0_REG 0x3FF59000;
#define LEDC_HSCH0_HPOINT_REG 0x3FF59004;
#define GPIO_ENABLE_W1TS 0x3FF44024;
#define LEDC_HSCH0_DUTY_REG 0x3FF59008;
#define LEDC_HSCH0_CONF1_REG 0x3FF5900C;
#define LEDC_HSTIMER0_CONF_REG 0x3FF59140;
#define LEDC_HSTIMER0_VALUE_REG 0x3FF59144;
#define LEDC_HSCH0_DUTY_R_REG 0x3FF59010;
#define LEDC_INT_RAW_REG 0x3FF59180;
#define LEDC_INT_ENA_REG 0x3FF59188;
void app_main() {
volatile uint32_t* interrupts_enable = LEDC_INT_ENA_REG;
*interrupts_enable |= (0b10001);
volatile uint32_t* timer_cfg = LEDC_HSTIMER0_CONF_REG;
*timer_cfg |= 8;
*timer_cfg |= 1<<13;
*timer_cfg |= 1<<25;
volatile uint32_t* reader_timer = LEDC_HSTIMER0_VALUE_REG;
volatile uint32_t* interrupt_reader = LEDC_INT_RAW_REG;
//printf("%" PRIX32 "\n", *timer_cfg);
volatile uint32_t* ls_cfg = 0x3FF59160;
*ls_cfg |= 8;
*ls_cfg |= 1<<13;
*ls_cfg |= 3<<25;
volatile uint32_t* reader_slow = 0x3FF59164;
while (true) {
printf("%" PRIu32 "timer\n",*reader_timer);
printf("%" PRIu32 "timer SLOW\n",*reader_slow);
printf("%" PRIu32 "interrupt\n",*interrupt_reader);
vTaskDelay(20 / portTICK_PERIOD_MS);
}
}
I've tried reading the documentation to understand what I'm missing but I was not able to get it since there are not practical examples online.
I expected that after configured the timer, the channel and the gpio the LED followed the duty cycle. When I saw nothing was happening I decided to focus on single spot and tried to print the timer value (it controls the duty cycle) but seems like the timer does not start.
I was able to make it work thanks to the advices of Lundin and JimmyB in the comments.
NOTE: This code works on my phisical Esp32(DEVKIT 1) since I got it. I have not tested it on the emulator.
What I was missing:
What I discovered:
I post here a working code in case someone gets stuck like me.
#include <stdio.h>
#include <unistd.h>
#include <inttypes.h>
#define GPIO_OUT_W1TS_REG (*(volatile uint32_t*)0x3FF44008)
#define GPIO_OUT_W1TC_REG (*(volatile uint32_t*)0x3FF4400C)
#define GPIO_ENABLE_W1TS_REG (*(volatile uint32_t*)0x3FF44024)
#define GPIO_ENABLE_W1TC_REG (*(volatile uint32_t*)0x3FF44028)
#define GPIO_FUNC0_OUT_SEL_CFG_REG 0x3FF44530
#define LEDC_CONF_REG (*(volatile uint32_t*)0x3FF59190)
#define LEDC_HSCH0_CONF0_REG (*(volatile uint32_t*)0x3FF59000)
#define LEDC_HSCH0_CONF1_REG (*(volatile uint32_t*)0x3FF5900C)
#define LEDC_HSCH0_DUTY_REG (*(volatile uint32_t*)0x3FF59008)
#define LEDC_HSCH0_DUTY_R_REG (*(volatile uint32_t*)0x3FF59010)
#define LEDC_HSCH0_HPOINT_REG (*(volatile uint32_t*)0x3FF59004)
#define LEDC_HSTIMER0_CONF_REG (*(volatile uint32_t*)0x3FF59140)
#define IO_MUX_GPIO26_REG (*(volatile uint32_t*)0x3FF49028)
#define DPORT_PERIP_CLK_EN_REG (*(volatile uint32_t*)0x3FF000C0)
#define DPORT_PERIP_RST_EN_REG (*(volatile uint32_t*)0x3FF000C4)
#define LEDC_HSTIMER0_VALUE_REG (*(volatile uint32_t*)0x3FF59144)
#define resolution (uint)8
void app_main(void)
{
printf("test\n");
DPORT_PERIP_CLK_EN_REG |= (1<<11);// enable clock for ledc
LEDC_HSTIMER0_CONF_REG &= ~(0xf);
LEDC_HSTIMER0_CONF_REG |= resolution; //resolution = 8 bit
uint divider = 80000000 / (5000 * 256);
LEDC_HSTIMER0_CONF_REG |= (divider<<13);
LEDC_HSCH0_CONF0_REG &= ~(0b00); //timer 0
LEDC_HSCH0_CONF0_REG |= (1<<2); //enale output channel
LEDC_HSCH0_HPOINT_REG = 1; // value to set high
LEDC_HSCH0_DUTY_REG &= ~(0xffffff);
LEDC_HSCH0_DUTY_REG |= (20<<4); // low duty cycle
uint low = 1; // flag to control next duty value
// gpio setting
volatile uint32_t* gpio26_cfg = (volatile uint32_t*)GPIO_FUNC0_OUT_SEL_CFG_REG + 26;
// peripheral 71 -> hschan0
*gpio26_cfg = 71;
GPIO_ENABLE_W1TS_REG |= (1<<26);
// function 2
IO_MUX_GPIO26_REG &= ~(0b111 << 12);
IO_MUX_GPIO26_REG |= (2<<12);
LEDC_HSCH0_CONF1_REG |= (1<<31); // start channel duty cycle
LEDC_HSTIMER0_CONF_REG &= ~(1<<24); //reset timer
uint counter = 0;
while (1) {
if (counter == 2){
if (low == 0) {
LEDC_HSCH0_DUTY_REG &= ~(0xffffff);
LEDC_HSCH0_DUTY_REG |= (30<<4);
low = 1;
LEDC_HSCH0_CONF1_REG |= (1<<31); // start channel duty cycle
} else {
LEDC_HSCH0_DUTY_REG &= ~(0xffffff);
LEDC_HSCH0_DUTY_REG |= (128<<4);
low = 0;
LEDC_HSCH0_CONF1_REG |= (1<<31); // start channel duty cycle
}
counter = 0;
}
printf("timer value: %" PRIu32 "\n", LEDC_HSTIMER0_VALUE_REG);
printf("duty value: %" PRIu32 "\n", LEDC_HSCH0_DUTY_R_REG);
printf("counter: %d\n", counter);
sleep(1);
counter++;
}
}
Since I am a newbie in C please feel free to correct me as Lundin did, I will appreciate it.