diff --git a/examples/0timerint/timer.c b/examples/0timerint/timer.c index 6de5238..3fb82b2 100644 --- a/examples/0timerint/timer.c +++ b/examples/0timerint/timer.c @@ -17,6 +17,7 @@ #include "main.h" #include "defines.h" #include "romfunctions.h" +#include "rtc.h" #include "typeaux.h" #include "uart.h" #include "utils/uartutils.h" @@ -242,6 +243,29 @@ static void _uart_cycle(uint64_t u64tckNow) { case 'i': uart_printf(&gsUART0, "Ccompare%u\r\n", gsMeasParam.eCcompare); break; + case 'z': // cycle through CPU_CPUPERIOD_SEL values + Reg rCpuPeriod = dport_regs()->CPU_PER_CONF & 3; + ++rCpuPeriod; + rCpuPeriod &= 3; + dport_regs()->CPU_PER_CONF = rCpuPeriod; + uart_printf(&gsUART0, "CPU_CPUPERIOD_SEL = %u\r\n", rCpuPeriod); + break; + case 'x': // cycle through clock sources + Reg rClkConf = gsRTC.CLK_CONF; + uart_printf(&gsUART0, "CLK_CONF = %08x\r\n", rClkConf); + uint32_t u32ClockSource = (rClkConf >> 27) & 3; + do { + ++u32ClockSource; + u32ClockSource &= 3; + } while (u32ClockSource == 0 || u32ClockSource == 2); + rClkConf &= ~(3 << 27); + rClkConf |= (u32ClockSource << 27); + // this will not work here: + // switching to any CLK SRC other than PLL will also cause change in APB frequency + // thus change in UART0 speed + // gsRTC.CLK_CONF = rClkConf; + uart_printf(&gsUART0, "RTC_CNTL_SOC_CLK_SEL = %u (%08x)\r\n", u32ClockSource, rClkConf); + break; // Alarm value modification case '>': @@ -312,7 +336,12 @@ static void _print_resultline(uint32_t u32Idx, const Result * psResult) { // -------------- Interface functions -------------- +// RTC_CNTL_SOC_CLK_SEL SoC clock selection. 0: XTAL, 1: PLL, 2: CK8M, 3: APLL. (R/W) +#define RTC_CNTL_SOC_CLK_SEL 1 + void prog_init_pro_pre() { + gsRTC.CLK_CONF = (RTC_CNTL_SOC_CLK_SEL << 27) | (2 << 12) | (1 << 9) | (4 << 1); + _uart_init(); } diff --git a/examples/1rmttm1637/README.md b/examples/1rmttm1637/README.md index 9df4793..9ca62b5 100644 --- a/examples/1rmttm1637/README.md +++ b/examples/1rmttm1637/README.md @@ -21,8 +21,8 @@ The inner subcycle is 6 steps long and is called **phase** (see `u8Phase`): #### Connections ``` -ESP32.GPIO21 -- X1.CLK -ESP32.GPIO19 -- X1.DIO +ESP32.GPIO25 -- X1.CLK +ESP32.GPIO26 -- X1.DIO ESP32.GND -- X1.GND ESP32.VCC -- X1.VCC ``` @@ -33,4 +33,6 @@ ESP32.VCC -- X1.VCC and the numbers are smoothly faded in / faded out. * First, a square should be displayed in the top left corner (`0x63`). -Then, this square should move around the display (clockwise). \ No newline at end of file +Then, this square should move around the display (clockwise). + +* Display everything (the 6 phases above) rotated by 180°. \ No newline at end of file diff --git a/examples/1rmttm1637/rmttm1637.c b/examples/1rmttm1637/rmttm1637.c index f74f2e6..43f7095 100644 --- a/examples/1rmttm1637/rmttm1637.c +++ b/examples/1rmttm1637/rmttm1637.c @@ -28,8 +28,8 @@ #define UART_FREQ_HZ 115200U // #2: Channels / wires / addresses -#define CLK_GPIO 21U -#define DIO_GPIO 19U +#define CLK_GPIO 25U +#define DIO_GPIO 26U #define CLK_CH RMT_CH1 #define DIO_CH RMT_CH0 @@ -43,7 +43,7 @@ typedef struct { STm1637State *psState; - uint64_t u64tckStart; + uint32_t u32tckStart; } SReadyCbParam; // ================ Local function declarations ================= @@ -69,10 +69,10 @@ static SReadyCbParam gsReadyData; static void _rmttm1637_ready(void *pvParam) { SReadyCbParam *psParam = (SReadyCbParam*) pvParam; - uint64_t u64TckStop = timg_ticks(gsTimer); - uart_printf(&gsUART0, "Display ready (failed ACKs: %03X)\tDt: %d ns\n", + uint32_t u32tckStop = (uint32_t)timg_ticks(gsTimer); + uart_printf(&gsUART0, "Display ready (failed ACKs: %03X)\tDt: %u ns\r\n", psParam->psState->abNak & 0xfff, - TICKS2NS((uint32_t) (u64TckStop - psParam->u64tckStart))); + TICKS2NS(u32tckStop - (uint32_t) psParam->u32tckStart)); } static void _rmttm1637_init() { @@ -100,18 +100,18 @@ static void _rmttm1637_cycle(uint64_t u64Ticks) { gau8Tm1637Data[i] = gau8NumToSeg[u8DatIdx]; gau8Tm1637Data[TM1637_COLON_POS] |= 0x80; } - gsReadyData.u64tckStart = timg_ticks(gsTimer); + gsReadyData.u32tckStart = (uint32_t) timg_ticks(gsTimer); tm1637_flush_full(&gsTm1637State, TM1637_CELLS); break; case 1: // remove colon/dot gau8Tm1637Data[TM1637_COLON_POS] &= 0x7f; - gsReadyData.u64tckStart = timg_ticks(gsTimer); + gsReadyData.u32tckStart = (uint32_t) timg_ticks(gsTimer); tm1637_flush_range(&gsTm1637State, TM1637_COLON_POS, 1); break; case 2: // low brightness case 3: // high brightness tm1637_set_brightness(&gsTm1637State, true, u8Phase == 2 ? 2 : 7); - gsReadyData.u64tckStart = timg_ticks(gsTimer); + gsReadyData.u32tckStart = (uint32_t) timg_ticks(gsTimer); tm1637_flush_brightness(&gsTm1637State); break; case 4: // display '-' after colon @@ -119,12 +119,12 @@ static void _rmttm1637_cycle(uint64_t u64Ticks) { gau8Tm1637Data[i] = 0x40; } gau8Tm1637Data[TM1637_COLON_POS] |= 0x80; - gsReadyData.u64tckStart = timg_ticks(gsTimer); + gsReadyData.u32tckStart = (uint32_t) timg_ticks(gsTimer); tm1637_flush_range(&gsTm1637State, TM1637_COLON_POS, TM1637_CELLS - TM1637_COLON_POS); break; default: // remove colon/dot gau8Tm1637Data[TM1637_COLON_POS] &= 0x7f; - gsReadyData.u64tckStart = timg_ticks(gsTimer); + gsReadyData.u32tckStart = (uint32_t) timg_ticks(gsTimer); tm1637_flush_range(&gsTm1637State, TM1637_COLON_POS, 1); } // jump to next state @@ -141,7 +141,6 @@ static void _rmttm1637_cycle(uint64_t u64Ticks) { } // -------------- Interface functions -------------- - void prog_init_pro_pre() { gsUART0.CLKDIV.raw = UART_HZ2CLKDIV(UART_FREQ_HZ, APB_FREQ_HZ); _rmttm1637_init(); diff --git a/modules/tm1637.c b/modules/tm1637.c index e7fe582..01eef96 100644 --- a/modules/tm1637.c +++ b/modules/tm1637.c @@ -33,11 +33,11 @@ * 4 MHz 666 KHz 103 µs 30 µs 500 KHz 138 µs 40 µs * 5 MHz 833 KHz 828 µs 24 µs 625 KHz 110 µs 32 µs */ -#define RMT_FREQ_KHZ 3000U -#define CLK_PERIOD_TICKS 6U - +#define TM1637_CLK_FREQ_KHZ 500U +#define RMT_FREQ_KHZ 4000U +#define CLK_PERIOD_TICKS (RMT_FREQ_KHZ / TM1637_CLK_FREQ_KHZ) #define CLK_HALFPERIOD_TICKS (CLK_PERIOD_TICKS / 2) -#define DIO_DELAY_TICKS 0U ///< delay of DIO signal change to CLK falling edge +#define DIO_DELAY_TICKS 1U ///< delay of DIO signal change to CLK falling edge // Commands #define CMD_SETDATA 0x40 @@ -67,11 +67,12 @@ static void _start_tx_process(STm1637State *psState, SInternals sX); // ==================== Local Data ================ // ==================== Implementation ================ + /** * When the DIO TX process is over, CLK TX is still running. * Here we have CLK_PERIOD_TICKS - DIO_DELAY_TICKS to do * some time consuming calculations. - * Note, here we update the RMT RAM of the CLK channel, however, as we modify olny its first 2 registers, + * Note, here we update the RMT RAM of the CLK channel, however, as we modify only its first 2 registers, * the update does not disturb the ongoing CLK TX process. * @param pvParam TM1637 state descriptor. */ @@ -135,15 +136,15 @@ static inline void _init_clkseq(ERmtChannel eChannel) { */ static inline void _update_clkseq(ERmtChannel eChannel, bool bStop, bool bStart) { RegAddr prClkRam = rmt_ram_addr(eChannel, 1, 0); // cppcheck-suppress unusedVariable - prClkRam[1] = RMT_ENTRYPAIR(0, CLK_HALFPERIOD_TICKS, 1, CLK_HALFPERIOD_TICKS); - if (bStart && !bStop) { + prClkRam[1] = RMT_ENTRYPAIR(0, CLK_HALFPERIOD_TICKS, 1, CLK_HALFPERIOD_TICKS); // the clk period of LSB (set to its initial value) + if (bStart && !bStop) { // very first byte prClkRam[0] = RMT_ENTRYPAIR(1, 1, 1, CLK_HALFPERIOD_TICKS - 1); // 0.5 periods - } else if (bStart && bStop) { + } else if (bStart && bStop) { // stop followed by start prClkRam[0] = RMT_ENTRYPAIR(0, CLK_HALFPERIOD_TICKS, 1, 3 * CLK_HALFPERIOD_TICKS); // 2 periods - } else if (!bStart && bStop) { + } else if (!bStart && bStop) { // only stop (terminating stop) prClkRam[0] = RMT_ENTRYPAIR(0, CLK_HALFPERIOD_TICKS, 1, 2 * CLK_HALFPERIOD_TICKS); // 1.5 periods prClkRam[1] = 0; - } else { + } else { // no start, no stop prClkRam[0] = RMT_ENTRYPAIR(0, 1, 0, 1); prClkRam[1] -= 2; } @@ -176,7 +177,7 @@ static inline void _dat_dioseq(RegAddr prDioRam, uint8_t u8Dat) { * @param u8Dat Bit sequence to represent on DIO (except for the case when STOP is not followed by START). */ static inline void _update_dioseq(ERmtChannel eChannel, bool bStop, bool bStart, uint8_t u8Dat) { - static uint32_t u32EntryPairStart = RMT_ENTRYPAIR(0, 1, 0, CLK_HALFPERIOD_TICKS - 1); // 0.5 periods + static uint32_t u32EntryPairStart = RMT_ENTRYPAIR(0, 1, 0, CLK_HALFPERIOD_TICKS - 1 + DIO_DELAY_TICKS); // 0.5 periods + delay static uint32_t u32EntryPairStop = RMT_ENTRYPAIR(0, 2 * CLK_HALFPERIOD_TICKS, 1, CLK_HALFPERIOD_TICKS); // 1.5 periods RegAddr prDioRam = rmt_ram_addr(eChannel, 1, 0); // cppcheck-suppress unusedVariable if (bStart && !bStop) { @@ -191,7 +192,7 @@ static inline void _update_dioseq(ERmtChannel eChannel, bool bStop, bool bStart, prDioRam[1] = 0; } else { _dat_dioseq(prDioRam, u8Dat); - prDioRam[0] + DIO_DELAY_TICKS; + prDioRam[0] += DIO_DELAY_TICKS; } } @@ -207,8 +208,7 @@ static void _next_byte(void *pvParam) { STm1637State *psParam = (STm1637State*) pvParam; ++psParam->sByteI.u8Cur; - rmt_start_tx(psParam->sIface.eClkCh, true); - rmt_start_tx(psParam->sIface.eDioCh, true); + rmt_start_tx_sync(psParam->sIface.eClkCh, psParam->sIface.eDioCh, true); } @@ -253,12 +253,6 @@ static void _rmt_config_channel(const STm1637Iface *psIface, uint8_t u8Divisor) } // ============== Interface functions ============== -/** - * Initializes a state object. - * @param psIface Defines TM1637 communication interface (GPIO pins, RMT channels). - * @param pu8Data Source of 7 segment characters to display. - * @return Initialized TM1637 state descriptor. - */ STm1637State tm1637_config(const STm1637Iface *psIface, uint8_t *pu8Data) { return (STm1637State){ .sIface = *psIface, @@ -267,11 +261,6 @@ STm1637State tm1637_config(const STm1637Iface *psIface, uint8_t *pu8Data) { .pvReadyCbArg = NULL}; } -/** - * Initializes TM1637 communication peripherals. - * @param psState TM1637 state descriptor. - * @param u32ApbClkFreq APB clock frequency (used to calculate RMT divisor). - */ void tm1637_init(STm1637State *psState, uint32_t u32ApbClkFreq) { rmt_init_channel(psState->sIface.eClkCh, psState->sIface.u8ClkPin, true); rmt_init_channel(psState->sIface.eDioCh, psState->sIface.u8DioPin, true); @@ -295,38 +284,15 @@ void tm1637_deinit(STm1637State *psState) { // release GPIO } -/** - * Sets TM1637 brightness value. - * Note, this function does not trigger communication. Either tm1637_flush_full() - * or tm1637_flush_brightness() must be invoked in order to apply changes. - * @param psState TM1637 state descriptor. - * @param bOn Turn display on (false: turn off). - * @param u8Value Valid range: [0..7] 0: lowest (1/16), 7: highest (14/16) brightness. - */ void tm1637_set_brightness(STm1637State *psState, bool bOn, uint8_t u8Value) { psState->u8Brightness = (bOn ? 0x08 : 0x00) | (u8Value & 0x07); } -/** - * Set callback function and parameter. - * This callback function will be invoked when any communication process is over. - * @param psState TM1637 state descriptor. - * @param fHandler Callback function. - * @param pvArg Parameter of the callback function. - */ void tm1637_set_readycb(STm1637State *psState, Isr fHandler, void *pvArg) { psState->fReadyCb = fHandler; psState->pvReadyCbArg = pvArg; } -/** - * Full flush consists of 3 TM1637 commands: - * 1. CMD_SETDATA: set write mode (with incremental addressing). - * 2. CMD_SETADDRESS: set initial address (and write data to display registers). - * 3. CMD_CTRLDISPLAY: set brightness. - * @param psState TM1637 state descriptor. - * @param u8Len Number of cells/characters to update in the display registers. - */ void tm1637_flush_full(STm1637State *psState, uint8_t u8Len) { // reset address mode and address psState->au8Bytes[0] = CMD_SETDATA; @@ -341,13 +307,6 @@ void tm1637_flush_full(STm1637State *psState, uint8_t u8Len) { _start_tx_process(psState, (SInternals){3 + u8Len, 3}); } -/** - * Limited display register update. - * A single command is sent (CMD_SETADDRESS) followed by data bytes. - * @param psState TM1637 state descriptor. - * @param u8Pos Start cell/character update at this position. - * @param u8Len Number of data bytes to send (number of cells/characters to update). - */ void tm1637_flush_range(STm1637State *psState, uint8_t u8Pos, uint8_t u8Len) { psState->au8Bytes[0] = CMD_SETADDRESS | (u8Pos & 0x07); for (uint8_t i = 0; i < u8Len; ++i) { @@ -357,12 +316,6 @@ void tm1637_flush_range(STm1637State *psState, uint8_t u8Pos, uint8_t u8Len) { _start_tx_process(psState, (SInternals){1 + u8Len, 1}); } -/** - * Sets brightness on the TM1637 device. - * A single command is sent (CMD_CTRLDISPLAY) without any data bytes. - * tm1637_set_brightness() must be called before this function. - * @param psState TM1637 state descriptor. - */ void tm1637_flush_brightness(STm1637State *psState) { psState->au8Bytes[0] = CMD_CTRLDISPLAY | (psState->u8Brightness & 0x0f); _start_tx_process(psState, (SInternals){1, 1}); diff --git a/modules/tm1637.h b/modules/tm1637.h index 5e17197..cac9923 100644 --- a/modules/tm1637.h +++ b/modules/tm1637.h @@ -45,13 +45,68 @@ extern "C" { } STm1637State; + /** + * Initializes a state object. + * @param psIface Defines TM1637 communication interface (GPIO pins, RMT channels). + * @param pu8Data Source of 7 segment characters to display. + * @return Initialized TM1637 state descriptor. + */ STm1637State tm1637_config(const STm1637Iface *psIface, uint8_t *pu8Data); + + /** + * Initializes TM1637 communication peripherals. + * @param psState TM1637 state descriptor. + * @param u32ApbClkFreq APB clock frequency, used for calculating RMT divisor (and implicitly TM1637 CLK frequency). + * The default TM1637 CLK frequency is 500KHz. If the real APB clk freq is 80.000.000, but u32ApbClkFreq is set to 40.000.000, + * the TM1637 CLK frequency will be 1MHz, etc. The TM1637 CLK frequency can be tuned up to 2.5MHz (experimental). + */ void tm1637_init(STm1637State *psState, uint32_t u32ApbClkFreq); void tm1637_deinit(STm1637State *psState); + + /** + * Sets TM1637 brightness value. + * Note, this function does not trigger communication. Either tm1637_flush_full() + * or tm1637_flush_brightness() must be invoked in order to apply changes. + * @param psState TM1637 state descriptor. + * @param bOn Turn display on (false: turn off). + * @param u8Value Valid range: [0..7] 0: lowest (1/16), 7: highest (14/16) brightness. + */ void tm1637_set_brightness(STm1637State *psState, bool bOn, uint8_t u8Value); + + /** + * Set callback function and parameter. + * This callback function will be invoked when any communication process is over. + * @param psState TM1637 state descriptor. + * @param fHandler Callback function. + * @param pvArg Parameter of the callback function. + */ void tm1637_set_readycb(STm1637State *psState, Isr fHandler, void *pvArg); + + /** + * Full flush consists of 3 TM1637 commands: + * 1. CMD_SETDATA: set write mode (with incremental addressing). + * 2. CMD_SETADDRESS: set initial address (and write data to display registers). + * 3. CMD_CTRLDISPLAY: set brightness. + * @param psState TM1637 state descriptor. + * @param u8Len Number of cells/characters to update in the display registers. + */ void tm1637_flush_full(STm1637State *psState, uint8_t u8Len); + + /** + * Limited display register update. + * A single command is sent (CMD_SETADDRESS) followed by data bytes. + * @param psState TM1637 state descriptor. + * @param u8Pos Start cell/character update at this position. + * @param u8Len Number of data bytes to send (number of cells/characters to update). + */ + void tm1637_flush_range(STm1637State *psState, uint8_t u8Pos, uint8_t u8Len); + /** + * Sets brightness on the TM1637 device. + * A single command is sent (CMD_CTRLDISPLAY) without any data bytes. + * tm1637_set_brightness() must be called before this function. + * @param psState TM1637 state descriptor. + */ void tm1637_flush_brightness(STm1637State *psState); #ifdef __cplusplus diff --git a/src/rmt.h b/src/rmt.h index 5f98b28..67a6fe0 100644 --- a/src/rmt.h +++ b/src/rmt.h @@ -237,6 +237,28 @@ extern "C" { gpsRMT->asChConf[eChannel].r1.raw |= rConf1.raw; } + /** + * Start transmit at two channels (nearly) simultaneously. + * @param eChannel0 Channel to start first. + * @param eChannel1 Channel to start second (with 2 instructions delay). + * @param bMemRdRst Should the controller reset its memory read address for the channels. + */ + static inline void rmt_start_tx_sync(ERmtChannel eChannel0, ERmtChannel eChannel1, bool bMemRdRst) { + // read (note, the dst variables are NOT volatiles) + uint32_t u32Ch0Conf1 = gpsRMT->asChConf[eChannel0].r1.raw; + uint32_t u32Ch1Conf1 = gpsRMT->asChConf[eChannel1].r1.raw; + + // set + u32Ch0Conf1 |= 1; + u32Ch0Conf1 |= bMemRdRst ? 8 : 0; + u32Ch1Conf1 |= 1; + u32Ch1Conf1 |= bMemRdRst ? 8 : 0; + + // write + gpsRMT->asChConf[eChannel0].r1.raw = u32Ch0Conf1; + gpsRMT->asChConf[eChannel1].r1.raw = u32Ch1Conf1; + } + /** * Start receiving data. * @param eChannel Identifies the channel.