I recently received a request regarding the use of PEEK and POKE functions, reminiscent of our oldest programming memories.
Bishopxxl has already shown us in a post how to invert the polarity of the TX and RX lines of the serial port, and I, inspired by a request from PeterN concerning the implementation of the DAC cosine waveform generator, thought this was an excellent opportunity to do some old-school programming by peeking and poking into registers.
For clarification, classic ESP32 chips are equipped with a CW (Cosine Waveform) module which, in conjunction with the DACs, allows for the generation of sinusoidal waveforms independently of the processor, thus without affecting any CPU resources.
This generator is capable of producing frequencies from 130Hz to approximately 100KHz.
Theoretically, it could reach 8MHz, but significant distortion becomes apparent at around 100KHz.
With a clever trick, by modifying the RTC Clock frequency via a divider, it's possible to reach a minimum of 16Hz, but this could potentially affect the operation of other parts of the CPU.
You can find a very interesting article here: https://github.com/krzychb/dac-cosine, which I used as a basis for my work.
I have therefore written a small program in BASIC that uses PEEK and POKE functions to activate and control this generator.
Here's a demonstration of how to use the internal cosine DAC generator, which works only on the classic ESP32 !
This program demonstrates how to use PEEK and POKE functions to directly manipulate the ESP32's registers, enabling and controlling the cosine waveform generator. It sets up both DAC channels, configures the frequency, scale, offset, and inversion settings for the waveform output.
ESP32 DAC Cosine Generator Demo
Code: [Local Link Removed for Guests]
'demo on how use the internal cosine DAC generator
' works only on the ESP32 classic
' for more details look at
' https://github.com/krzychb/dac-cosine
' Technical documentation available here (chapter 'Cosine Waveform Generator')
' https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
RTC_FAST_CLK_FREQ_APPROX = 8500000
DR_REG_SENS_BASE = 0x3ff48800
DR_REG_RTCCNTL_BASE = 0x3ff48000
RTC_CNTL_CLK_CONF_REG = DR_REG_RTCCNTL_BASE + 0x70
SENS_SAR_DAC_CTRL1_REG = DR_REG_SENS_BASE + 0x98
SENS_SAR_DAC_CTRL2_REG = DR_REG_SENS_BASE + 0x9C
pin.dac 25,0 ' enable the DAC1
pin.dac 26,0 ' enable the DAC2
'dac_cosine_enable
'Enable tone generator common to both channels
SET_PERI_REG_MASK SENS_SAR_DAC_CTRL1_REG, 16 ' set bit SENS_SW_TONE_EN
'Enable / connect tone tone generator on / to this channel
SET_PERI_REG_MASK SENS_SAR_DAC_CTRL2_REG, 24 ' set bit SENS_DAC_CW_EN1_M
'Invert MSB, otherwise part of waveform will have inverted
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0x3, 2, 20 ' SENS_DAC_INV1_S (invert MSB)
' DAC Channel 2
SET_PERI_REG_MASK SENS_SAR_DAC_CTRL2_REG, 25 ' set bit SENS_DAC_CW_EN2_M
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0x3, 2, 22 ' SENS_DAC_INV2_S (invert MSB)
'dac_frequency_set
'/*
'* Set frequency of internal CW generator common to both DAC channels
'*
'* clk_8m_div: 0b000 - 0b111
'* frequency_step: range 0x0001 - 0xFFFF
'*/
clk_8m_div = 0 ' can be from 0 to 7
frequency_step = 772 ' can be from 1 to 65535
SET_PERI_REG_BITS RTC_CNTL_CLK_CONF_REG, 0x7, clk_8m_div, 12 ' set RTC_CNTL_CK8M_DIV_SEL
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL1_REG, 0xffff, frequency_step, 0 ' set frequency_step
frequency = RTC_FAST_CLK_FREQ_APPROX / (1 + clk_8m_div) * frequency_step / 65536
wlog "The frequency is ", frequency
print "The frequency is ", frequency
'dac_scale_set
'/*
' * Scale output of a DAC channel using two bit pattern:
' *
' * - 00: no scale
' * - 01: scale to 1/2
' * - 10: scale to 1/4
' * - 11: scale to 1/8
' */
scale1 = 0
scale2 = 1
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0x3, scale1, 16 ' set SENS_DAC_SCALE1
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0x3, scale2, 18 ' set SENS_DAC_SCALE2
'dac_offset_set
'/*
' * Offset output of a DAC channel
' *
' * Range 0x00 - 0xFF
' */
offset1 = 0
offset2 = 0
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0xff, offset1, 0 ' set SENS_DAC_DC1
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0xff, offset2, 8 ' set SENS_DAC_DC2
'dac_invert_set
'/*
' * Invert output pattern of a DAC channel
' *
' * - 00: does not invert any bits,
' * - 01: inverts all bits,
' * - 10: inverts MSB,
' * - 11: inverts all bits except for MSB
' */
invert1 = 2
invert2 = 3
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0x3, invert1, 20 ' set SENS_DAC_INV1
SET_PERI_REG_BITS SENS_SAR_DAC_CTRL2_REG, 0x3, invert2, 22 ' set SENS_DAC_INV2
end
'functions used to write into registers
sub SET_PERI_REG_MASK(reg,mask)
local r, v
r = bas.peek(reg)
v = 1 << mask
bas.poke reg, r or v
end sub
sub CLEAR_PERI_REG_MASK(reg,mask)
local r, v
r = bas.peek(reg)
v = 1 << mask
bas.poke reg, r and (not v)
end sub
sub SET_PERI_REG_BITS(reg,bit_map,value,shift)
local r, mask, v
r = bas.peek(reg)
mask = bit_map << shift
r = r and (not mask)
v = (value and bit_map) << shift
r = r or v
bas.poke reg, r
end sub
For Peeking and Poking enthusiasts, this document (https://www.espressif.com/sites/default ... ual_en.pdf) can serve as a source of inspiration.
There are tons of registers to modify, but... well, good luck with that!