Directly writing control registers- new programmer feeling pretty lost

BJHenry
Posts: 2
Joined: Fri Oct 20, 2017 3:10 am

Directly writing control registers- new programmer feeling pretty lost

Postby BJHenry » Sun Oct 22, 2017 7:30 am

Hi all
I started playing around with Arduino for work about two years ago and have quite enjoyed it. It was my first exposure to a C style language so there was quite a learning curve at the beginning. I've reached the stage where I used direct manipulation of the registers to control some things- for instance I built a controller for a 3 axis touch probe machine, in which I wrote directly to the registers related to three of the Timers, along with setting up interrupts for the timers, to drive the three stepper motors. As an example the code looked something like this:

Code: Select all

TCCR0A = 0x102;		//Timer0 CTC mode, A clear on match
TCCR0B = 	0x1;			//Timer0 clock = no prescaler
OCR0A = 0xF8;			//Timer0 count up to value


I stumbled on the ESP32 recently and it looks great. I love the speed, connectivity options and the RMT module. I also like the fact that it can be programmed from the Arduino IDE. I've bought a Heltec WifiKit32 and have successfully gotten it working and had a bit of a play with it, writing to the onboard OLED etc. So far, so good.

The trouble comes when I try to use the RMT. I've read through the Technical Reference and the Datasheet and feel that I've got a reasonable understanding of how to make the RMT work. The trouble is, if I try to set one of the configuration registers I get an error. For example here I'll use the RMT_TX_START_CH0 register, which is set to 1 to start the RMT module channel 0 to start transmitting:

Code: Select all

RMT_TX_START_CH1 = 1;
If I try to compile a sketch with this it gives the following error:
lvalue required as left operand of assignment
This is where I get a bit lost. Since all that I've learned of C comes from Arduino I know that I've got holes in my knowledge and this is one of them. Is there some other syntax I should be using to write the configuration registers? Do I have to do something else since the registers here are 32bit rather than 8?
I'm pretty lost at sea here, any help would be greatly appreciated.

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: Directly writing control registers- new programmer feeling pretty lost

Postby kolban » Sun Oct 22, 2017 7:00 pm

Device register programming is a complicated topic and really requires a pretty solid grounding in both theory and architecture. In a nutshell, the ESP32 is an MCU with a lot of embedded peripherals. These peripherals are controlled by reading and writing memory locations. Each peripheral has a set of memory locations described in the ESP32 Technical Reference manual. By writing to a memory location, you are effectively controlling the operation of that peripheral. I wouldn't suggest that you think about these as "registers" as those have a very distinct meaning which is not what we are talking about. However, these are control values and writing new values into the memory locations affects the operation.

Rather than talk about a memory location by its address (eg. 0x12345678) we can logically think of a memory location as having a symbolic name. eg. the RMT_TX_START_CH1 address. The header files that come with ESP-IDF (and if you don't know what that means, you'll need to study up on that) provides pre-defined definitions for those addresses and hence we (programmers) don't need to think in numeric values but rather in symbolics.

If we have a variable that holds an address

eg.
uint8_t* RMT_TX_START_CH1 = 0x12345678

Then we can change the value by writing into it:

*RMT_TX_START_CH1 = myNewValue;

This is standard C.

If there are many control values in sequential memory, they are typically aggregated into a C structure and the C structure's address points to the start of the memory of the control group.

The registers you were using in Arduino land were based on the ATMel processors (I think) and (opinion) broke the nice clean model of Arduino. If we say that Arduino is an environment (editor) and APIs (libraries) then attempting to use ATmel specific technologies is not a great idea as you lose portability.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Directly writing control registers- new programmer feeling pretty lost

Postby WiFive » Sun Oct 22, 2017 10:35 pm

RMT_TX_START_CH1 is not a register, it is a bit in the register RMT_CH1CONF1_REG.

So you could do REG_SET_BIT(RMT_CH1CONF1_REG, RMT_TX_START_CH1)

BJHenry
Posts: 2
Joined: Fri Oct 20, 2017 3:10 am

Re: Directly writing control registers- new programmer feeling pretty lost

Postby BJHenry » Mon Oct 23, 2017 8:27 am

First off, thanks to the both of you for your replies. Kolban, your documentation is very helpful even for an absolute amateur like me. WiFive, your suggestion of the REG_SET_BIT command lead me to finding soc.h which has been very enlightening.
I've spent a lot of time on Google and Youtube last night and today and feel that I've got a better grasp of API usage. Certainly not perfect, but much improved. Between Kolban's documentation, the ESP32 programming guide and what I've learned from looking at the internal documentation in the Arduino ESP32 core I've actually managed to get the RMT working in a fashion.
This code fires the onboard LED two times.

Code: Select all

#include <driver/rmt.h>
  
void setup() 
{
  rmt_config_t rmt_tx;
  rmt_tx.channel = (rmt_channel_t)0;
  rmt_tx.gpio_num = 25;                   //onboard visible LED
  rmt_tx.mem_block_num = 1;
  rmt_tx.clk_div = 250;
  rmt_tx.tx_config.loop_en = false;
  rmt_tx.tx_config.carrier_duty_percent = 50;
  rmt_tx.tx_config.carrier_freq_hz = 38000;
  rmt_tx.tx_config.carrier_level = (rmt_carrier_level_t)1;
  rmt_tx.tx_config.carrier_en = false;                    // \/Fix for below issue?
//  rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;      //Not in rmt driver at all?
  rmt_tx.tx_config.idle_level = (rmt_idle_level_t)0;
  rmt_tx.tx_config.idle_output_en = true;
  rmt_tx.rmt_mode = (rmt_mode_t)0;
  rmt_config(&rmt_tx);
  
  rmt_item32_t items[3];
  items[0].duration0 = 32767;
  items[0].level0 = 1;
  items[0].duration1 = 32767;
  items[0].level1 = 0;
  items[1].duration0 = 32767;
  items[1].level0 = 1;
  items[1].duration1 = 32767;
  items[1].level1 = 0;

//install system RMT driver, disable rx ringbuffer for transmitter.
  rmt_driver_install(rmt_tx.channel, 0, 0);

  rmt_write_items((rmt_channel_t) 0, (rmt_item32_t*) items,4,false);
}

void loop() 
{
}
If I change clk_div I can speed it up or slow it down, and if I change loop_en= true then the led continues to blink constantly.
I'd like to be able to use rmt_fill_tx_items() and rmt_tx_start() since the data that I want to send won't be changing. I think that I'm having trouble with the memory offset in rmt_fill_tx_items(). rmt.reg.h says that the offset should be:

Code: Select all

DR_REG_RMT_BASE + 0x800 + 64 * 4 * (i)
and soc.h says that:

Code: Select all

#define DR_REG_RMT_BASE  0x3ff56000
but when I tried using
rmt_fill_tx_items((rmt_channel_t) 0, (rmt_item32_t*) items, 4, (0x3ff56000 + 0x800 + 0x40 * 4);
rmt_tx_start((rmt_channel_t) 0, true);
I got some random flashes that didn't repeat when loop_en = true, so I think it must not have been reading from the right RAM location.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Directly writing control registers- new programmer feeling pretty lost

Postby WiFive » Mon Oct 23, 2017 12:45 pm

Offset means local offset within the memory block not the actual address within the global memory space. You're working too hard to try and undo all the convenience of the sdk functions.

ESPpeter
Posts: 4
Joined: Mon Dec 04, 2017 12:49 pm

Re: Directly writing control registers- new programmer feeling pretty lost

Postby ESPpeter » Mon Dec 04, 2017 1:17 pm

Hi!
How can write DIRECTLY the registers?
*((volatile uint32_t *) (0x3ff560F0)) = 3 do nothing.
Why?
But the read work:
... = *((volatile uint32_t *) (0x3ff560F0))
Thanks

ESP_igrr
Posts: 2072
Joined: Tue Dec 01, 2015 8:37 am

Re: Directly writing control registers- new programmer feeling pretty lost

Postby ESP_igrr » Tue Dec 05, 2017 3:16 am

ESPpeter: Make sure you enable RMT peripheral clock before writing to the control registers. I.e. call periph_module_enable(PERIPH_RMT_MODULE);

ESPpeter
Posts: 4
Joined: Mon Dec 04, 2017 12:49 pm

Re: Directly writing control registers- new programmer feeling pretty lost

Postby ESPpeter » Tue Dec 05, 2017 1:43 pm

ESP_igrr wrote:ESPpeter: Make sure you enable RMT peripheral clock before writing to the control registers. I.e. call periph_module_enable(PERIPH_RMT_MODULE);
Thanks!
You win! :)
*((volatile uint32_t *) (0x3FF000C0)) |= 0x200 ;
*((volatile uint32_t *) (0x3FF000C4)) &= (~(0x200)) ;
after them work!
:)

Who is online

Users browsing this forum: No registered users and 65 guests