Skip to content

Instantly share code, notes, and snippets.

@garthk
Last active July 25, 2018 07:55
Show Gist options
  • Save garthk/aafec9ce014e8621c03dd4db32587ccc to your computer and use it in GitHub Desktop.
Save garthk/aafec9ce014e8621c03dd4db32587ccc to your computer and use it in GitHub Desktop.
ESP8266_RTOS_SDK PDM ESP8266 playing 512Hz sine wave over UART
#include <stdio.h>
#include <stdint.h>
#include <esp_libc.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "rom/ets_sys.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_system.h"
uint32_t FAKEPWM[32] = {
0x00000010, 0x00000410, 0x00400410, 0x00400C10,
0x00500C10, 0x00D00C10, 0x20D00C10, 0x21D00C10,
0x21D80C10, 0xA1D80C10, 0xA1D80D10, 0xA1D80D30,
0xA1DC0D30, 0xA1DC8D30, 0xB1DC8D30, 0xB9DC8D30,
0xB9FC8D30, 0xBDFC8D30, 0xBDFE8D30, 0xBDFE8D32,
0xBDFE8D33, 0xBDFECD33, 0xFDFECD33, 0xFDFECD73,
0xFDFEDD73, 0xFFFEDD73, 0xFFFEDD7B, 0xFFFEFD7B,
0xFFFFFD7B, 0xFFFFFDFB, 0xFFFFFFFB, 0xFFFFFFFF
};
int WAVELENGTH[] = {
31, 30, 30, 30, 30, 30, 30, 29, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21,
20, 19, 18, 17, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3,
2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 29, 30, 30, 30, 30, 30, 31
};
int WAVELENGTH_N = sizeof(WAVELENGTH) / sizeof(int);
#define ESP8266_UART_DEPTH 128
#define ESP8266_UART_HWM (ESP8266_UART_DEPTH - 2 * sizeof(uint32_t))
/* Both UART0 and UART1 have a length of 128 Byte hardware, read and write FIFO operations are at the same address */
/* The serial of ESP8266 can support the baud rate range from 300 to 115200 * 40. */
/******************************************************************************
* FunctionName : app_main
* Description : entry of user application, init user function here
* Parameters : none
* Returns : none
*******************************************************************************/
typedef struct {
uint NextSampleIndex;
uint32_t BytesSinceLastReset;
} WaveProgress;
u_char* get_next_sample(WaveProgress *arg) {
uint idx = arg->NextSampleIndex;
if (idx == (WAVELENGTH_N - 1)) {
arg->NextSampleIndex = 0;
} else {
arg->NextSampleIndex = idx + 1;
}
uint8_t sample = WAVELENGTH[idx];
return (u_char *)(FAKEPWM + sample);
}
uint32_t depth() {
// ref: uart_tx_one_char in uart.c
uint32_t fifo_cnt = READ_PERI_REG(UART_STATUS(UART1)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S);
return fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT;
}
void add_more(WaveProgress *arg) {
while (true) {
if (depth() < ESP8266_UART_HWM) {
u_char *quad = get_next_sample(arg);
WRITE_PERI_REG(UART_FIFO(UART1), *quad++);
WRITE_PERI_REG(UART_FIFO(UART1), *quad++);
WRITE_PERI_REG(UART_FIFO(UART1), *quad++);
WRITE_PERI_REG(UART_FIFO(UART1), *quad++);
arg->BytesSinceLastReset += 4;
} else {
return;
}
}
}
void empty_handler(WaveProgress *arg) {
uint32_t uart_intr_status = READ_PERI_REG(UART_INT_ST(UART1));
if (UART_TXFIFO_EMPTY_INT_ST == (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST)) {
WRITE_PERI_REG(UART_INT_CLR(UART1), UART_TXFIFO_EMPTY_INT_CLR);
add_more(arg);
}
}
void user_init(void) {
// OLD NAME NEVER USE
}
void app_main(void) { // is this run in a task?
printf("SDK version:%s\n", esp_get_idf_version());
printf("High water mark: %d\n", ESP8266_UART_HWM);
WaveProgress arg;
arg.BytesSinceLastReset = 0;
arg.NextSampleIndex = 0;
for (int i = 0; i < WAVELENGTH_N * 2; i++) {
u_char *quad = get_next_sample(&arg);
u_char a = *quad++;
u_char b = *quad++;
u_char c = *quad++;
u_char d = *quad++;
// flip byte order so we can recognise our constants in FAKEPWM above
printf("%02d %02d 0x%02x%02x%02x%02x\n", i, arg.NextSampleIndex, d, c, b, a);
}
// DO NOT uart_init_new
// ETS_UART_INTR_DISABLE();
UART_WaitTxFifoEmpty(UART0);
UART_WaitTxFifoEmpty(UART1);
UART_ResetFifo(UART1);
UART_ConfigTypeDef uart1;
uart1.baud_rate = BIT_RATE_1843200; // close-ish to 44100 * 32 for our first test
uart1.data_bits = UART_WordLength_8b;
uart1.parity = USART_Parity_None;
uart1.stop_bits = 1; // 0 doesn't work
uart1.flow_ctrl = USART_HardwareFlowControl_None;
UART_ParamConfig(UART1, &uart1);
UART_IntrConfTypeDef uart1intr;
uart1intr.UART_IntrEnMask = UART_TXFIFO_EMPTY_INT_ENA;
uart1intr.UART_RX_FifoFullIntrThresh = 0;
uart1intr.UART_RX_TimeOutIntrThresh = 0;
uart1intr.UART_TX_FifoEmptyIntrThresh = ESP8266_UART_DEPTH / 2;
UART_IntrConfig(UART1, &uart1intr);
UART_intr_handler_register(&empty_handler, &arg);
printf("UART initialised. Enabling interrupts...\n");
ETS_UART_INTR_ENABLE(); // seems to be specific to UART0?
printf("Enabled interrupts.\n");
add_more(&arg);
while(1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
printf("%d\n", arg.BytesSinceLastReset);
arg.BytesSinceLastReset = 0;
}
}
@garthk
Copy link
Author

garthk commented Jul 25, 2018

Make a copy of ESP8266_RTOS_SDK/examples/get-started/project_template, copy user_main.c into main/, and make menuconfig flash monitor. If you hook up a resistor and two capacitors between your UART1 TX pin and a speaker, you'll hear a 512Hz tone. If you hook a logic analyser up to that pin, you'll see the pulse density modulated version of the wave. We can't turn off the start and stop bits, but that doesn't much affect the sound quality and it drags the speed closer to the intended 490Hz despite the baud rate not being quite right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment