Hi,
To make a long story short, I borrowed some code from a project ( https://github.com/jacqu/teensyshot ) to generate a DSHOT signal. I'm currently adapting this code for my own project using a Teensy 4.1 board. Note that DSHOT protocol is used to control a motor via an ESC (Electronic Speed Controller), but this is not really important for my question here.
So, using eFlexPWM / DMA to generate the signal on an output pin, it works fine with some pins however it does not work at all with some other pins. So, I'm not able to figure out why it is not working with some pins.
So, I created the simplest as possible program below to demonstrate the problem. If you run the program below as is, you will see that the signal is correctly generated on pin 4. If you comment out the line #define TEST_PIN_4 then uncomment line #define TEST_PIN_22, you will see that the program works fine as well but that time generating signal on pin 22.
Now, if you comment out back line #define TEST_PIN_22 and you uncomment line #define TEST_PIN_2 , #define TEST_PIN_3 or #define TEST_PIN_9 (uncomment only one line pin at a time) you will see that signal is not generated on those pins.
So, is somebody could help me to understand what I missed? It should work fine on those pins too.
Thank you.
To make a long story short, I borrowed some code from a project ( https://github.com/jacqu/teensyshot ) to generate a DSHOT signal. I'm currently adapting this code for my own project using a Teensy 4.1 board. Note that DSHOT protocol is used to control a motor via an ESC (Electronic Speed Controller), but this is not really important for my question here.
So, using eFlexPWM / DMA to generate the signal on an output pin, it works fine with some pins however it does not work at all with some other pins. So, I'm not able to figure out why it is not working with some pins.
So, I created the simplest as possible program below to demonstrate the problem. If you run the program below as is, you will see that the signal is correctly generated on pin 4. If you comment out the line #define TEST_PIN_4 then uncomment line #define TEST_PIN_22, you will see that the program works fine as well but that time generating signal on pin 22.
Now, if you comment out back line #define TEST_PIN_22 and you uncomment line #define TEST_PIN_2 , #define TEST_PIN_3 or #define TEST_PIN_9 (uncomment only one line pin at a time) you will see that signal is not generated on those pins.
So, is somebody could help me to understand what I missed? It should work fine on those pins too.
Code:
// Minimal test program to create a dshot 600 protocol signal. (for a support request purpose)
// Some code borrowed from https://github.com/jacqu/teensyshot
#include <Arduino.h>
#include "DMAChannel.h"
#define F_TMR F_BUS_ACTUAL // teensy 4
#define DSHOT_DMA_LENGTH 18 // Number of steps of one DMA sequence (the two last values are zero)
#define DSHOT_DMA_MARGIN 2 // Number of additional bit duration to wait until checking if DMA is over
#define DSHOT_DSHOT_LENGTH 16 // Number of bits in a DSHOT sequence
#define DSHOT_BT_DURATION 1670 // Duration of 1 DSHOT600 bit in ns
#define DSHOT_LP_DURATION 1250 // Duration of a DSHOT600 long pulse in ns
#define DSHOT_SP_DURATION 625 // Duration of a DSHOT600 short pulse in ns
#define DSHOT_MAX_VALUE 2047 // Maximum DSHOT value
const uint16_t DSHOT_short_pulse = uint64_t(F_TMR) * DSHOT_SP_DURATION / 1000000000; // DSHOT short pulse duration (nb of F_BUS periods)
const uint16_t DSHOT_long_pulse = uint64_t(F_TMR) * DSHOT_LP_DURATION / 1000000000; // DSHOT long pulse duration (nb of F_BUS periods)
const uint16_t DSHOT_bit_length = uint64_t(F_TMR) * DSHOT_BT_DURATION / 1000000000; // DSHOT bit duration (nb of F_BUS periods)
// **************************************
// Begin define test pins
//
// Uncomment only one TEST_PIN_? at a time below to do a test with that specific pin.
#define TEST_PIN_4 //Works fine
//#define TEST_PIN_22 //Works fine
//#define TEST_PIN_2 //Does not work ??? Help needed with that pin
//#define TEST_PIN_3 //Does not work ??? Help needed with that pin
//#define TEST_PIN_9 //Does not work ??? Help needed with that pin
//
// End define test pins
// **************************************
#ifdef TEST_PIN_4
volatile uint8_t my_eFlexPWM_pin = 4; // Output pin: 4 = EMC_06 = FLEXPWM2_PWM0_A ALT1 //See Table 10-1. Muxing Options at page 297
volatile IMXRT_FLEXPWM_t *my_eFlexPWM_module = &IMXRT_FLEXPWM2; // FLEXPWM2_PWM0_A -> FLEXPWM2
volatile uint8_t my_eFlexPWM_submodule = 0; // FLEXPWM2_PWM0_A -> _PWM0 = submodule 0
volatile uint8_t my_eFlexPWM_submodule_channel = 0; // FLEXPWM2_PWM0_A -> _A = A=0, B=1, X=2
volatile uint8_t my_eFlexPWM_mux_alt = 1; // ALT1 -> 1
volatile uint8_t my_eFlexPWM_mux_dma_source = DMAMUX_SOURCE_FLEXPWM2_WRITE0; //Table 4-3. DMA MUX Mapping, page 52
#endif
#ifdef TEST_PIN_22
volatile uint8_t my_eFlexPWM_pin = 22; // Output pin: 22 = AD_B1_08 = FLEXPWM4_PWM0_A ALT1 //See Table 10-1. Muxing Options at page 298
volatile IMXRT_FLEXPWM_t *my_eFlexPWM_module = &IMXRT_FLEXPWM4; // FLEXPWM4_PWM0_A -> FLEXPWM4
volatile uint8_t my_eFlexPWM_submodule = 0; // FLEXPWM4_PWM0_A -> _PWM0 = submodule 0
volatile uint8_t my_eFlexPWM_submodule_channel = 0; // FLEXPWM2_PWM0_A -> _A = A=0, B=1, X=2
volatile uint8_t my_eFlexPWM_mux_alt = 1; // ALT1 -> 1
volatile uint8_t my_eFlexPWM_mux_dma_source = DMAMUX_SOURCE_FLEXPWM4_WRITE0; //Table 4-3. DMA MUX Mapping, page 52
#endif
#ifdef TEST_PIN_2
volatile uint8_t my_eFlexPWM_pin = 2; // Output pin: 2 = EMC_04 = FLEXPWM4_PWM2_A ALT1 //See Table 10-1. Muxing Options at page 298
volatile IMXRT_FLEXPWM_t *my_eFlexPWM_module = &IMXRT_FLEXPWM4; // FLEXPWM4_PWM2_A -> FLEXPWM4
volatile uint8_t my_eFlexPWM_submodule = 2; // FLEXPWM4_PWM2_A -> _PWM2 = submodule 2
volatile uint8_t my_eFlexPWM_submodule_channel = 0; // FLEXPWM4_PWM2_A -> _A = A=0, B=1, X=2
volatile uint8_t my_eFlexPWM_mux_alt = 1; // ALT1 -> 1
volatile uint8_t my_eFlexPWM_mux_dma_source = DMAMUX_SOURCE_FLEXPWM4_WRITE0; //Table 4-3. DMA MUX Mapping, page 52
#endif
#ifdef TEST_PIN_3
volatile uint8_t my_eFlexPWM_pin = 3; // Output pin: 3 = EMC_05 = FLEXPWM4_PWM2_B ALT1 //See Table 10-1. Muxing Options at page 298
volatile IMXRT_FLEXPWM_t *my_eFlexPWM_module = &IMXRT_FLEXPWM4; // FLEXPWM4_PWM2_B -> FLEXPWM4
volatile uint8_t my_eFlexPWM_submodule = 2; // FLEXPWM4_PWM2_B -> _PWM2 = submodule 2
volatile uint8_t my_eFlexPWM_submodule_channel = 1; // FLEXPWM4_PWM2_B -> _B = A=0, B=1, X=2
volatile uint8_t my_eFlexPWM_mux_alt = 1; // ALT1 -> 1
volatile uint8_t my_eFlexPWM_mux_dma_source = DMAMUX_SOURCE_FLEXPWM4_WRITE0; //Table 4-3. DMA MUX Mapping, page 52
#endif
#ifdef TEST_PIN_9
volatile uint8_t my_eFlexPWM_pin = 9; // Output pin: 9 = B0_11 = FLEXPWM2_PWM2_B ALT2 //See Table 10-1. Muxing Options at page 298
volatile IMXRT_FLEXPWM_t *my_eFlexPWM_module = &IMXRT_FLEXPWM2; // FLEXPWM2_PWM2_B -> FLEXPWM2
volatile uint8_t my_eFlexPWM_submodule = 2; // FLEXPWM2_PWM2_B -> _PWM2 = submodule 2
volatile uint8_t my_eFlexPWM_submodule_channel = 1; // FLEXPWM2_PWM2_B -> _B = A=0, B=1, X=2
volatile uint8_t my_eFlexPWM_mux_alt = 2; // ALT2 -> 2
volatile uint8_t my_eFlexPWM_mux_dma_source = DMAMUX_SOURCE_FLEXPWM2_WRITE0; //Table 4-3. DMA MUX Mapping, page 52
#endif
// DMA objects
DMAChannel myDMAChannel;
// DMA data
volatile uint16_t DSHOT_dma_data[DSHOT_DMA_LENGTH] = { 0 };
elapsedMillis sinceChange;
uint16_t lastDshotCommandSent = 0;
// Send the DSHOT signal to the configured channel
int DSHOT_send( uint16_t dshotCommand) {
int j;
int requestTelemetry = 0;
uint16_t data;
// Initialize DMA buffer
// Compute the packet to send
// 11 first MSB = command
// 12th MSB = telemetry request
// 4 LSB = CRC
data = ( dshotCommand << 5 ) | ( requestTelemetry << 4 );
data |= ( ( data >> 4 ) ^ ( data >> 8 ) ^ ( data >> 12 ) ) & 0x0f;
// Generate DSHOT timings corresponding to the packet
for ( j = 0; j < DSHOT_DSHOT_LENGTH; j++ ) {
if ( data & ( 1 << ( DSHOT_DSHOT_LENGTH - 1 - j ) ) ) {
DSHOT_dma_data[j] = DSHOT_long_pulse;
} else {
DSHOT_dma_data[j] = DSHOT_short_pulse;
}
}
// Clear error flag on DMA channel
myDMAChannel.clearError( );
return 0;
}
void setup() {
// Configure pin on the board as DSHOT output
// This pin is configured as eFlexPWM (FLEXPWMn) PWM output
*(portConfigRegister( my_eFlexPWM_pin )) = my_eFlexPWM_mux_alt;
// Configure eFlexPWM module and submodule for PWM generation
// --- submodule specific registers ---
// INIT: initial counter value
// VAL0: PWM_X compare value
// VAL1: counter max value
// VAL2: must be 0 for edge-aligned PWM
// VAL3: PWM_A compare value
// VAL4: must be 0 for edge-aligned PWM
// VAL5: PWM_B compare value
// OCTRL: invert polarity of PWMq FLEXPWM_SMOCTRL_POLq
// DMAEN: FLEXPWM_SMDMAEN_VALDE to enable DMA
// --- module specific registers ---
// OUTEN: output enable for submodule n and PWM q FLEXPWM_OUTEN_PWMq_EN( 1 << n )
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].INIT = 0;
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL0 = 0;
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL1 = DSHOT_bit_length;
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL2 = 0;
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL3 = 0;
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL4 = 0;
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL5 = 0;
if ( my_eFlexPWM_submodule_channel == 2 ) { //A=0, B=1, X=2
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].OCTRL = FLEXPWM_SMOCTRL_POLX;
(*my_eFlexPWM_module).OUTEN |= FLEXPWM_OUTEN_PWMX_EN(1 << my_eFlexPWM_submodule);
} else if ( my_eFlexPWM_submodule_channel == 1 ) { //A=0, B=1, X=2
(*my_eFlexPWM_module).OUTEN |= FLEXPWM_OUTEN_PWMB_EN(1 << my_eFlexPWM_submodule);
} else { //A=0, B=1, X=2
(*my_eFlexPWM_module).OUTEN |= FLEXPWM_OUTEN_PWMA_EN(1 << my_eFlexPWM_submodule);
}
(*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].DMAEN = FLEXPWM_SMDMAEN_VALDE;
// DMA channel is linked to a unique eFlexPWM submodule
// DMA channel is triggered by independant hardware events
myDMAChannel.sourceBuffer( DSHOT_dma_data, DSHOT_DMA_LENGTH * sizeof( uint16_t ) );
if ( my_eFlexPWM_submodule_channel == 2 ) {
myDMAChannel.destination( (uint16_t&) (*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL0 );
} else if ( my_eFlexPWM_submodule_channel == 1 ) {
myDMAChannel.destination( (uint16_t&) (*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL5 );
} else {
myDMAChannel.destination( (uint16_t&) (*my_eFlexPWM_module).SM[my_eFlexPWM_submodule].VAL3 );
}
myDMAChannel.triggerAtHardwareEvent( my_eFlexPWM_mux_dma_source );
myDMAChannel.enable( );
DSHOT_send(lastDshotCommandSent);
}
void loop() {
if (sinceChange > 1000) { //Just change the dshot signal each second to see the signal changing on my scope.
if (lastDshotCommandSent == 0) {
lastDshotCommandSent = 2047; //Command 2047 = Motor 100% Throttle
} else {
lastDshotCommandSent = 0; //Command 0 = Motor stop
}
DSHOT_send(lastDshotCommandSent);
sinceChange = 0;
}
}
Thank you.