Hello,
I wrote a routine to send an SDLC frame (NRZI) via PWM in serialised mode to replace TX of a Zilog ESCC (with GPIO cycles) originally on the CM4 and now on the RPI5 (RP1). This mode is also used for the WS2812.
DMA here is not used, FIFO is quite large (16 x 32 bits on the BCM2711).
I had a few issues with the RP1:
1> To see if everything is sent, you need to test the number of characters in the FIFO, the Empty bit comes way too early.
2> Does clearing FIFO sometimes have the opposite effect, or I maybe have to wait for the result? (I removed the sequence)
3> Instead of putting 32 bits in range, I have to put 31!
Otherwise it works, Here are the sources:
I wrote a routine to send an SDLC frame (NRZI) via PWM in serialised mode to replace TX of a Zilog ESCC (with GPIO cycles) originally on the CM4 and now on the RPI5 (RP1). This mode is also used for the WS2812.
DMA here is not used, FIFO is quite large (16 x 32 bits on the BCM2711).
I had a few issues with the RP1:
1> To see if everything is sent, you need to test the number of characters in the FIFO, the Empty bit comes way too early.
2> Does clearing FIFO sometimes have the opposite effect, or I maybe have to wait for the result? (I removed the sequence)
3> Instead of putting 32 bits in range, I have to put 31!
Otherwise it works, Here are the sources:
Code:
#include <string.h>#include <stdint.h>#include "FreeRTOS.h"#include "BCM2835/lib/synchronize.h"#include "BCM2835/drivers/rpi_pwm.h"#include "BCM2835/drivers/rpi_pwm_rp1.h"#include "BCM2835/drivers/rpi_gpio.h"#include "BCM2835/drivers/rpi_gpio_clk.h"#include "BCM2835/drivers/rpi_clk_rp1.h"#include "BCM2835/drivers/rpi_systimer.h"#define GPIO_TX 18 // PWMtypedef struct { union { struct { uint16_t duration :15; uint16_t level :1; }; uint16_t val; };} rmt_item16_t;#define SYNC 0x55#define FLAG 0x7E#define NUM_PREAMBLE 1#define NUM_POSTAMBLE 1#define MAX_SIZE 64#define DEFAULT_BITRATE 375000static uint32_t bitrate = 0;static rmt_item16_t item_buf[MAX_SIZE * 8];static unsigned char tmp_buf[MAX_SIZE + 2];static uint32_t out_buf[MAX_SIZE + 1];/* calculate time of pulse */static inline void make_pulse(rmt_item16_t *item16, int du, int lv){ item16->duration = du; item16->level = lv;}/* make pulses from data byte */static int byte_to_pulse(rmt_item16_t *item16, int item16_size, uint8_t data, int *dup, int *lvp, int bsflag){ int i = 0; // index of item16[] uint8_t b; int du = *dup; int lv = *lvp; for(b = 1; b != 0; b <<= 1) { // scan data byte, LSb first if(b & data) { // check data bit // send 1 du++; if(bsflag && (du >= 6)) { // insert 0 (bit stuffing) if(i >= item16_size) break; make_pulse(&item16[i++], du, lv); du = 1; // bit stuffing 0 lv = !lv; } } else { // send 0 if(i >= item16_size) break; make_pulse(&item16[i++], du, lv); // make '1' pulse du = 1; // next '0' pulse duration lv = !lv; // invert level } } *dup = du; *lvp = lv; return i; // number of item16}/* * make NRZI pulses from packet data */static int make_nrzi_pulses(rmt_item16_t *item16, int item16_size, unsigned char data[], int data_len){ int i; int du = 1; // duration, always start 0 data bit int lv = 1; // level, idle level is 1 int idx = 0; // index of item16[] to be stored next pulse /* preamble, bit stuffing off */ for(i = 0; i < NUM_PREAMBLE; i++) idx += byte_to_pulse(&item16[idx], item16_size - idx, FLAG, &du, &lv, 0); // no bit stuffing /* packet data, bit stuffing on */ for(i = 0; i < data_len; i++) /* char data */ idx += byte_to_pulse(&item16[idx], item16_size - idx, data[i], &du, &lv, 1); // insert bit stuffing /* postamble, bit stuffing off */ for(i = 0; i < NUM_POSTAMBLE; i++) idx += byte_to_pulse(&item16[idx], item16_size - idx, FLAG, &du, &lv, 0); // no bit stuffing // end mark if(idx < item16_size) { item16[idx].duration = 0; // 0 means end of pulses item16[idx].level = 0; idx++; } return idx; // return value is number of item}#define INITFCS 0xFFFF#define GOODFCS 0xF0B8static unsigned short fcstab[256] = { /* 00 */ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, /* 08 */ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, /* 10 */ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, /* 18 */ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, /* 20 */ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, /* 28 */ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, /* 30 */ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, /* 38 */ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, /* 40 */ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, /* 48 */ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, /* 50 */ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, /* 58 */ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, /* 60 */ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, /* 68 */ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, /* 70 */ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, /* 78 */ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, /* 80 */ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, /* 88 */ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, /* 90 */ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, /* 98 */ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, /* a0 */ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, /* a8 */ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, /* b0 */ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, /* b8 */ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, /* c0 */ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, /* c8 */ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, /* d0 */ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, /* d8 */ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, /* e0 */ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, /* e8 */ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, /* f0 */ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, /* f8 */ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};/* * HDLC FCS computation. Read RFC 1171 Appendix B and CCITT X.25 section * 2.27 for further details. */static unsigned short sdlc_build_crc(unsigned char *buffer, int size){ unsigned short fcs = INITFCS; while(size--) fcs = (fcs >> 8) ^ fcstab[(fcs ^ *buffer++) & 0xff]; return(~fcs);}void sdlc_init(uint32_t baud){ unsigned ref = rpi_gpio_get_clk_rate(GPIOClockSourcePLLD); unsigned divi = ref / baud; unsigned divf = (unsigned)((((double)ref / (double)baud) - (double)divi) * 4096.); if(rpi_type < 5) { rpi_gpio_clk_stop(GPIOClockPWM); rpi_gpio_clk_start(GPIOClockPWM, GPIOClockSourcePLLD, divi, divf, divf ? 1 : 0); rpi_gpio_sel_fun(GPIO_TX, RPI_GPIO_FSEL_ALT5); }#ifdef AARCH64 else { rpi_clk_rp1_start_rate(RPI_RP1_CLK_PWM0, baud); rpi_gpio_sel_fun(GPIO_TX, RPI_GPIO_FSEL_ALT3); /* PWM0 channel 2 */ }#endif /* AARCH64 */ bitrate = baud;}int sdlc_send_frame(unsigned char *buffer, int buffer_size){ uint32_t len = 0, start = 0; int i, j, n, bit, nb = 0, current_ipl; unsigned short fcs; if((buffer == NULL) || (buffer_size >= MAX_SIZE)) return(-1); memcpy(tmp_buf, buffer, buffer_size); memset(out_buf, 0, sizeof(out_buf)); fcs = sdlc_build_crc(buffer, buffer_size); tmp_buf[buffer_size] = (unsigned char)fcs; tmp_buf[buffer_size + 1] = (unsigned char)(fcs >> 8); n = make_nrzi_pulses(item_buf, sizeof(item_buf) / sizeof(rmt_item16_t), tmp_buf, buffer_size + 2); len += 3; len >>= 2; // 32 bits alignment bit = 15; // MSB first - 2 x SYNC out_buf[len] = (SYNC << 24) | (SYNC << 16); for(i = 0; i < n; i++) { if(!item_buf[i].duration && i) { if(item_buf[i-1].level) out_buf[len] |= (1 << bit); else out_buf[len] &= ~(1 << bit); len++; break; } for(j = 0; j < (int)item_buf[i].duration; j++) { if(!item_buf[i].level) out_buf[len] |= (1 << bit); // polarity inverted else out_buf[len] &= ~(1 << bit); bit--; if(bit < 0) { bit = 31; len++; if(len >= (sizeof(out_buf) / sizeof(uint32_t))) return(-1); } nb++; } } len++; // Last data in FIFO if(!bitrate) sdlc_init(DEFAULT_BITRATE); current_ipl = vPortSetIPL(portIPL_MAX); dmb(); if(rpi_type < 5) { // Clear PWM control & status register RPI_PWM->CTL = 0; // Set transmission range (32 bits) RPI_PWM->RNG1 = 32; // Enable serializer mode / Clear the FIFO / Use FIFO rather than DAT1 RPI_PWM->CTL = RPI_PWM_CTL_MODE1 | RPI_PWM_CTL_POLA1 | RPI_PWM_CTL_USEF1 | RPI_PWM_CTL_CLRF1; /* Serialiser mode, Repeat Last Data, Polarity, Use FIFO, Clear FIFO */ // Enable PWM RPI_PWM->CTL |= RPI_PWM_CTL_PWEN1; // Fill the FIFO for(i = 0; i < len; RPI_PWM->FIF1 = out_buf[i++]); }#ifdef AARCH64 else if(rp1_ok) { // Set transmission range (32 bits) RPI_PWM0_RP1->COMMON_RANGE = 31; // MSB serialiser output RPI_PWM0_RP1->CHAN2_CTRL = RPI_PWM_RP1_CHAN_CTRL_FIFO_POP | RPI_PWM_RP1_CHAN_CTRL_USEFIFO | RPI_PWM_RP1_CHAN_CTRL_BIND | RPI_PWM_RP1_CHAN_CTRL_INVERT | (4 << RPI_PWM_RP1_CHAN_CTRL_MODE_SHIFT); // Enable PWM RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN2_EN; RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_SET_UPDATE; // Fill the FIFO for(i = 0; i < len; RPI_PWM0_RP1->DUTY_FIFO = out_buf[i++]); }#endif /* AARCH64 */ vPortSetIPL(current_ipl); return(0);}int sdlc_send_frame_ok(void){#ifdef AARCH64 if(rpi_type >= 5) { if(rp1_ok) return((((RPI_PWM0_RP1->FIFO_CTRL & RPI_PWM_RP1_FIFO_CTRL_LEVEL_MASK) >> RPI_PWM_RP1_FIFO_CTRL_LEVEL_SHIFT) == 0) ? 1 : 0 ); return(0); }#endif return((RPI_PWM->STA & RPI_PWM_STA_EMPT1) ? 1 : 0 );}
Code:
#ifndef _RPI_PWM_RP1_H_#define _RPI_PWM_RP1_H_#include "rpi_base.h"#define RPI_PWM0_RP1_BASE (uintptr_t)(MEM_PCIE_RANGE_START + 0x98000)#define RPI_PWM1_RP1_BASE (uintptr_t)(MEM_PCIE_RANGE_START + 0x9C000)//// PWM fifo control register//#define RPI_PWM_RP1_FIFO_CTRL_LEVEL_SHIFT0#define RPI_PWM_RP1_FIFO_CTRL_LEVEL_MASK(0x1F << 0)#define RPI_PWM_RP1_FIFO_CTRL_FLUSH(1 << 5)#define RPI_PWM_RP1_FIFO_CTRL_FLUSH_DONE(1 << 6)#define RPI_PWM_RP1_FIFO_CTRL_THRESHOLD_SHIFT11#define RPI_PWM_RP1_FIFO_CTRL_THRESHOLD_MASK(0x1F << 11)#define RPI_PWM_RP1_FIFO_CTRL_DWELL_TIME_SHIFT16#define RPI_PWM_RP1_FIFO_CTRL_DWELL_TIME_MASK(0x1F << 16)#define RPI_PWM_RP1_FIFO_CTRL_DREQ_EN(1 << 31)//// PWM global control register//#define RPI_PWM_RP1_GLOBAL_CTRL_CHAN0_EN(1 << 0)#define RPI_PWM_RP1_GLOBAL_CTRL_CHAN1_EN(1 << 1)#define RPI_PWM_RP1_GLOBAL_CTRL_CHAN2_EN(1 << 2)#define RPI_PWM_RP1_GLOBAL_CTRL_CHAN3_EN(1 << 3)#define RPI_PWM_RP1_GLOBAL_CTRL_SET_UPDATE(1 << 31)//// PWM channel control register//#define RPI_PWM_RP1_CHAN_CTRL_MODE_SHIFT0#define RPI_PWM_RP1_CHAN_CTRL_MODE_MASK(7 << 0)#define RPI_PWM_RP1_CHAN_CTRL_INVERT(1 << 3)#define RPI_PWM_RP1_CHAN_CTRL_BIND(1 << 4)#define RPI_PWM_RP1_CHAN_CTRL_USEFIFO(1 << 5)#define RPI_PWM_RP1_CHAN_CTRL_SDM(1 << 6)#define RPI_PWM_RP1_CHAN_CTRL_DITHER(1 << 7)#define RPI_PWM_RP1_CHAN_CTRL_FIFO_POP(1 << 8)#define RPI_PWM_RP1_CHAN_CTRL_SDM_BITWIDTH_SHIFT12#define RPI_PWM_RP1_CHAN_CTRL_SDM_BITWIDTH(15 << 12)#define RPI_PWM_RP1_CHAN_CTRL_SDM_BIAS_SHIFT16#define RPI_PWM_RP1_CHAN_CTRL_SDM_BIAS_MASK (0xFFFF < 16)//// PWM interrupt raw//#define RPI_PWM_RP1_INTR_FIFO_UNDERFLOW(1 << 0)#define RPI_PWM_RP1_INTR_FIFO_OVERFLOW(1 << 1)#define RPI_PWM_RP1_INTR_FIFO_EMPTY(1 << 2)#define RPI_PWM_RP1_INTR_FIFO_FULL(1 << 3)#define RPI_PWM_RP1_INTR_DREQ_ACTIVE(1 << 4)#define RPI_PWM_RP1_INTR_CHAN0_RELOAD(1 << 5)#define RPI_PWM_RP1_INTR_CHAN1_RELOAD(1 << 6)#define RPI_PWM_RP1_INTR_CHAN2_RELOAD(1 << 7)#define RPI_PWM_RP1_INTR_CHAN3_RELOAD(1 << 8)typedef struct { volatile uint32_t GLOBAL_CTRL;///< 0x00 Global control bits volatile uint32_t FIFO_CTRL;///< 0x04 FIFO thresholding and status volatile uint32_t COMMON_RANGE;///< 0x08 volatile uint32_t COMMON_DUTY;///< 0x0c volatile uint32_t DUTY_FIFO;///< 0x10 volatile uint32_t CHAN0_CTRL;///< 0x14 Channel 0 control register volatile uint32_t CHAN0_RANGE;///< 0x18 volatile uint32_t CHAN0_PHASE;///< 0x1c volatile uint32_t CHAN0_DUTY;///< 0x20 volatile uint32_t CHAN1_CTRL;///< 0x24 Channel 1 control register volatile uint32_t CHAN1_RANGE;///< 0x28 volatile uint32_t CHAN1_PHASE;///< 0x2c volatile uint32_t CHAN1_DUTY;///< 0x30 volatile uint32_t CHAN2_CTRL;///< 0x34 Channel 2 control register volatile uint32_t CHAN2_RANGE;///< 0x38 volatile uint32_t CHAN2_PHASE;///< 0x3c volatile uint32_t CHAN2_DUTY;///< 0x40 volatile uint32_t CHAN3_CTRL ;///< 0x44 Channel 3 control register volatile uint32_t CHAN3_RANGE;///< 0x48 volatile uint32_t CHAN3_PHASE;///< 0x4c volatile uint32_t CHAN3_DUTY;///< 0x50 volatile uint32_t INTR;///< 0x54 Raw Interrupts volatile uint32_t INTE;///< 0x58 Interrupt Enable volatile uint32_t INTF;///< 0x5c Interrupt Force volatile uint32_t INTS;///< 0x60 Interrupt status after masking & forcing} RPI_PWM_RP1_t;#define RPI_PWM0_RP1((RPI_PWM_RP1_t *) RPI_PWM0_RP1_BASE)#define RPI_PWM1_RP1((RPI_PWM_RP1_t *) RPI_PWM1_RP1_BASE)extern long VirtualSwitch;static inline void rpi_pwm0_rp1(int ch, uint32_t period, uint32_t duty_cycle, int polarity, int enable){ uint32_t ctrl = RPI_PWM_RP1_CHAN_CTRL_FIFO_POP | (1 << RPI_PWM_RP1_CHAN_CTRL_MODE_SHIFT); if(polarity) ctrl |= RPI_PWM_RP1_CHAN_CTRL_INVERT; switch(ch) { case 0: RPI_PWM0_RP1->CHAN0_RANGE = period; RPI_PWM0_RP1->CHAN0_DUTY = duty_cycle; RPI_PWM0_RP1->CHAN0_CTRL = ctrl; if(enable) RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN0_EN; else RPI_PWM0_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN0_EN; break; case 1: RPI_PWM0_RP1->CHAN1_RANGE = period; RPI_PWM0_RP1->CHAN1_DUTY = duty_cycle; RPI_PWM0_RP1->CHAN1_CTRL = ctrl; if(enable) RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN1_EN; else RPI_PWM0_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN1_EN; break; case 2: RPI_PWM0_RP1->CHAN2_RANGE = period; RPI_PWM0_RP1->CHAN2_DUTY = duty_cycle; RPI_PWM0_RP1->CHAN2_CTRL = ctrl; if(enable) RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN2_EN; else RPI_PWM0_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN2_EN; break; case 3: RPI_PWM0_RP1->CHAN3_RANGE = period; RPI_PWM0_RP1->CHAN3_DUTY = duty_cycle; RPI_PWM0_RP1->CHAN3_CTRL = ctrl; if(enable) RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN3_EN; else RPI_PWM0_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN3_EN; break; } RPI_PWM0_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_SET_UPDATE;}static inline void rpi_pwm1_rp1(int ch, uint32_t period, uint32_t duty_cycle, int polarity, int enable){ uint32_t ctrl = RPI_PWM_RP1_CHAN_CTRL_FIFO_POP | (1 << RPI_PWM_RP1_CHAN_CTRL_MODE_SHIFT); if(polarity) ctrl |= RPI_PWM_RP1_CHAN_CTRL_INVERT; switch(ch) { case 0: RPI_PWM1_RP1->CHAN0_RANGE = period; RPI_PWM1_RP1->CHAN0_DUTY = duty_cycle; RPI_PWM1_RP1->CHAN0_CTRL = ctrl; if(enable) RPI_PWM1_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN0_EN; else RPI_PWM1_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN0_EN; break; case 1: RPI_PWM1_RP1->CHAN1_RANGE = period; RPI_PWM1_RP1->CHAN1_DUTY = duty_cycle; RPI_PWM1_RP1->CHAN1_CTRL = ctrl; if(enable) RPI_PWM1_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN1_EN; else RPI_PWM1_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN1_EN; break; case 2: RPI_PWM1_RP1->CHAN2_RANGE = period; RPI_PWM1_RP1->CHAN2_DUTY = duty_cycle; RPI_PWM1_RP1->CHAN2_CTRL = ctrl; if(enable) RPI_PWM1_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN2_EN; else RPI_PWM1_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN2_EN; break; case 3: RPI_PWM1_RP1->CHAN3_RANGE = period; RPI_PWM1_RP1->CHAN3_DUTY = duty_cycle; RPI_PWM1_RP1->CHAN3_CTRL = ctrl; if(enable) RPI_PWM1_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_CHAN3_EN; else RPI_PWM1_RP1->GLOBAL_CTRL &= ~RPI_PWM_RP1_GLOBAL_CTRL_CHAN3_EN; break; } RPI_PWM1_RP1->GLOBAL_CTRL |= RPI_PWM_RP1_GLOBAL_CTRL_SET_UPDATE;}#endif /* _RPI_PWM_RP1_H_ */
Statistics: Posted by aniplay — Sat Mar 09, 2024 12:58 pm — Replies 0 — Views 18