babc5a24e3
Co-authored-by: Copilot <copilot@github.com>
447 lines
13 KiB
C++
447 lines
13 KiB
C++
#include "stm32g4xx.h"
|
|
|
|
#include "gpio.h"
|
|
|
|
#include "dshot2.h"
|
|
|
|
#define DSHOT_FREQ_SCALE 10'000'000
|
|
|
|
#define DSHOT_FRAME_DATA 16
|
|
#define DSHOT_FRAME_SIZE 20
|
|
|
|
#define DSHOT_COUNT 4
|
|
|
|
#define DSHOT_TIM 2
|
|
|
|
#define DSHOT_TIM2 0
|
|
#define DSHOT_TIM1 1
|
|
|
|
#define DSHOT_CCR 2
|
|
#define DSHOT_CCR1 0
|
|
#define DSHOT_CCR2 1
|
|
#define DSHOT_CCR3 0
|
|
#define DSHOT_CCR4 1
|
|
|
|
static bool DSHOT_Bidirect;
|
|
|
|
static unsigned short DSHOT_Shift, DSHOT_PeriodTX, DSHOT_PeriodRX, DSHOT_Bit_0, DSHOT_Bit_1;
|
|
|
|
static unsigned short DSHOT_TX_Buff[DSHOT_TIM][DSHOT_FRAME_SIZE*DSHOT_CCR]; // TIM | CCR1 + CCR2
|
|
|
|
#define DSHOT_RX_SAMP 4
|
|
#define DSHOT_RX_SIZE 32
|
|
|
|
static unsigned short DSHOT_RX_Buff[DSHOT_RX_SIZE*DSHOT_RX_SAMP];
|
|
static GPIO_TypeDef* DSHOT_RX_Port;
|
|
static unsigned long DSHOT_RX_Mode, DSHOT_TX_Mode;
|
|
static unsigned short DSHOT_RX_Index[DSHOT_COUNT];
|
|
|
|
// Таблица декодирования GCR (DShot RPM)
|
|
static const unsigned char GCR_DECODE_TABLE[32]
|
|
{
|
|
[0x19] = 0x0, // 11001b -> 0
|
|
[0x1B] = 0x1, // 11011b -> 1
|
|
[0x12] = 0x2, // 10010b -> 2
|
|
[0x13] = 0x3, // 10011b -> 3
|
|
[0x1D] = 0x4, // 11101b -> 4
|
|
[0x15] = 0x5, // 10101b -> 5
|
|
[0x16] = 0x6, // 10110b -> 6
|
|
[0x17] = 0x7, // 10111b -> 7
|
|
[0x1A] = 0x8, // 11010b -> 8
|
|
[0x09] = 0x9, // 01001b -> 9
|
|
[0x0A] = 0xA, // 01010b -> A
|
|
[0x0B] = 0xB, // 01011b -> B
|
|
[0x1E] = 0xC, // 11110b -> C
|
|
[0x0D] = 0xD, // 01101b -> D
|
|
[0x0E] = 0xE, // 01110b -> E
|
|
[0x0F] = 0xF, // 01111b -> F
|
|
|
|
// Остальные значения 0xFF (ошибки)
|
|
[0x00] = 0xFF, [0x01] = 0xFF, [0x02] = 0xFF, [0x03] = 0xFF,
|
|
[0x04] = 0xFF, [0x05] = 0xFF, [0x06] = 0xFF, [0x07] = 0xFF,
|
|
[0x08] = 0xFF, [0x0C] = 0xFF, [0x10] = 0xFF, [0x11] = 0xFF,
|
|
[0x14] = 0xFF, [0x18] = 0xFF, [0x1C] = 0xFF, [0x1F] = 0xFF
|
|
};
|
|
|
|
inline unsigned char decodeGCR(unsigned char gcr_val)
|
|
{
|
|
return GCR_DECODE_TABLE[gcr_val & 0x1F]; // gcr_val должен быть от 0 до 31 (5 бит)
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static unsigned long CalcERPM(unsigned long code)
|
|
{
|
|
code = code ^ (code>>1);
|
|
|
|
const unsigned long mask = 0x1F;
|
|
|
|
unsigned long gcr1 = (code >> 0) & mask;
|
|
unsigned long gcr2 = (code >> 5) & mask;
|
|
unsigned long gcr3 = (code >> 10) & mask;
|
|
unsigned long gcr4 = (code >> 15) & mask;
|
|
|
|
gcr1=decodeGCR(gcr1);
|
|
gcr2=decodeGCR(gcr2);
|
|
gcr3=decodeGCR(gcr3);
|
|
gcr4=decodeGCR(gcr4);
|
|
|
|
unsigned long crc = (~(gcr2 ^ gcr3 ^ gcr4)) & 0x0F;
|
|
|
|
bool valid = crc==gcr1;
|
|
|
|
if (!valid || gcr1==0xFF || gcr2==0xFF || gcr3==0xFF || gcr4==0xFF) return -1;
|
|
|
|
unsigned short data = (gcr4 << 8) | (gcr3 << 4) | gcr2;
|
|
|
|
unsigned long e = (data >> 9) & 0x07; // Старшие 3 бита
|
|
unsigned long m = data & 0x1FF;
|
|
|
|
unsigned long p = m<<e;
|
|
|
|
if (p==0) return -1;
|
|
|
|
return 1'000'000/p;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void Begin_ModeRX()
|
|
{
|
|
TIM6->ARR = 2300; // 20us
|
|
TIM6->EGR = TIM_EGR_UG;
|
|
TIM6->SR = 0;
|
|
TIM6->DIER = TIM_DIER_UIE;
|
|
TIM6->CR1 = TIM_CR1_CEN;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
extern "C" void DMA2_Channel1_IRQHandler()
|
|
{
|
|
unsigned long sr = DMA2->ISR;
|
|
if(sr & DMA_ISR_TCIF1)
|
|
{
|
|
DMA2->IFCR = DMA_IFCR_CTCIF1;
|
|
|
|
Begin_ModeRX();
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
extern "C" void TIM6_DAC_IRQHandler()
|
|
{
|
|
DSHOT_RX_Port->MODER &= DSHOT_RX_Mode;
|
|
TIM6->ARR = DSHOT_PeriodRX;
|
|
TIM6->EGR = TIM_EGR_UG;
|
|
TIM6->DIER = TIM_DIER_UDE;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
static void Begin_ModeTX()
|
|
{
|
|
DSHOT_RX_Port->MODER |= DSHOT_TX_Mode;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
extern "C" void DMA2_Channel3_IRQHandler()
|
|
{
|
|
unsigned long sr = DMA2->ISR;
|
|
if(sr & DMA_ISR_TCIF3)
|
|
{
|
|
DMA2->IFCR = DMA_IFCR_CTCIF3;
|
|
TIM6->DIER = 0;
|
|
|
|
Begin_ModeTX();
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool DSHOT_Mode(unsigned long Freq, bool Bidirect)
|
|
{
|
|
if(Freq<150'000) Freq=150'000;
|
|
|
|
DSHOT_PeriodTX=SystemCoreClock/Freq;
|
|
DSHOT_PeriodRX=(DSHOT_PeriodTX-DSHOT_PeriodTX/4)/DSHOT_RX_SAMP;
|
|
|
|
DSHOT_Bit_0 = DSHOT_PeriodTX*379/1024; // 37%
|
|
DSHOT_Bit_1 = DSHOT_PeriodTX*767/1024; // 75%
|
|
|
|
DSHOT_Bidirect=Bidirect;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Режим приема
|
|
static bool Init_ModeRX()
|
|
{
|
|
if(RCC->APB1ENR1 & RCC_APB1ENR1_TIM6EN) return false;
|
|
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM6EN;
|
|
|
|
if(DMAMUX1_Channel8->CCR & DMAMUX_CxCR_DMAREQ_ID_Msk) return false;
|
|
DMAMUX1_Channel8->CCR = (8 << DMAMUX_CxCR_DMAREQ_ID_Pos); // TIM6_UP
|
|
|
|
DMA2_Channel3->CCR = 0;
|
|
DMA2_Channel3->CPAR = (unsigned long)&GPIOA->IDR;
|
|
DMA2_Channel3->CMAR = (unsigned long)DSHOT_RX_Buff;
|
|
DMA2_Channel3->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_TCIE;
|
|
|
|
NVIC_SetPriority(DMA2_Channel3_IRQn, 0);
|
|
NVIC_EnableIRQ(DMA2_Channel3_IRQn);
|
|
|
|
NVIC_SetPriority(TIM6_DAC_IRQn, 0);
|
|
NVIC_EnableIRQ(TIM6_DAC_IRQn);
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Режим передачи
|
|
static void Init_TIM2_TX()
|
|
{
|
|
TIM2->CR1 = 0;
|
|
TIM2->SMCR = (6 << TIM_SMCR_SMS_Pos); // Slaver
|
|
TIM2->PSC = 0;
|
|
TIM2->ARR = DSHOT_PeriodTX;
|
|
TIM2->CCMR1 = (6 << TIM_CCMR1_OC1M_Pos) | TIM_CCMR1_OC1PE | (6 << TIM_CCMR1_OC2M_Pos) | TIM_CCMR1_OC2PE;
|
|
TIM2->CCR1=TIM2->CCR2=0;
|
|
TIM2->EGR = TIM_EGR_UG;
|
|
if(DSHOT_Bidirect) TIM2->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC2E | TIM_CCER_CC2P;
|
|
else TIM2->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;
|
|
TIM2->DIER = TIM_DIER_UDE;
|
|
TIM2->DCR = (0x0D << TIM_DCR_DBA_Pos) | (1 << TIM_DCR_DBL_Pos); // Burst CCR1 & CCR2
|
|
|
|
TIM2->BDTR = TIM_BDTR_MOE;
|
|
|
|
DMAMUX1_Channel6->CCR = (60 << DMAMUX_CxCR_DMAREQ_ID_Pos); // TIM2_UP
|
|
|
|
// TX DMA TIM2
|
|
DMA2_Channel1->CPAR = (unsigned long)&TIM2->DMAR;
|
|
DMA2_Channel1->CMAR = (unsigned long)DSHOT_TX_Buff[DSHOT_TIM2];
|
|
DMA2_Channel1->CCR = DMA_CCR_DIR | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_1;
|
|
|
|
if(DSHOT_Bidirect) DMA2_Channel1->CCR |= DMA_CCR_TCIE;
|
|
|
|
NVIC_SetPriority(DMA2_Channel1_IRQn, 0);
|
|
|
|
NVIC_EnableIRQ(DMA2_Channel1_IRQn);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void Init_TIM1_TX()
|
|
{
|
|
TIM1->CR1 = 0;
|
|
TIM1->CR2 |= (0x01 << TIM_CR2_MMS_Pos); // Master генерирует TRGO при включении (CEN=1)
|
|
TIM1->PSC = 0;
|
|
TIM1->ARR = DSHOT_PeriodTX;
|
|
TIM1->CCMR2 = (6 << TIM_CCMR2_OC3M_Pos) | TIM_CCMR2_OC3PE | (6 << TIM_CCMR2_OC4M_Pos) | TIM_CCMR2_OC4PE;
|
|
TIM1->CCR3=TIM1->CCR4=0;
|
|
TIM1->EGR = TIM_EGR_UG;
|
|
if(DSHOT_Bidirect) TIM1->CCER = TIM_CCER_CC3E | TIM_CCER_CC3P | TIM_CCER_CC4E | TIM_CCER_CC4P;
|
|
else TIM1->CCER = TIM_CCER_CC3E | TIM_CCER_CC4E;
|
|
TIM1->DIER = TIM_DIER_UDE;
|
|
TIM1->DCR = (0x0F << TIM_DCR_DBA_Pos) | (1 << TIM_DCR_DBL_Pos); // Burst CCR3 & CCR4
|
|
|
|
TIM1->BDTR = TIM_BDTR_MOE;
|
|
|
|
DMAMUX1_Channel7->CCR = (46 << DMAMUX_CxCR_DMAREQ_ID_Pos); // TIM1_UP
|
|
|
|
// TX DMA TIM1
|
|
DMA2_Channel2->CPAR = (unsigned long)&TIM1->DMAR;
|
|
DMA2_Channel2->CMAR = (unsigned long)DSHOT_TX_Buff[DSHOT_TIM1];
|
|
DMA2_Channel2->CCR |= DMA_CCR_DIR | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool DSHOT_Init(unsigned long Freq, bool Bidirect)
|
|
{
|
|
if(RCC->APB2ENR & RCC_APB2ENR_TIM1EN) return false;
|
|
if(RCC->APB1ENR1 & RCC_APB1ENR1_TIM2EN) return false;
|
|
|
|
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
|
|
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN;
|
|
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN | RCC_AHB1ENR_DMAMUX1EN;
|
|
|
|
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
|
|
|
|
GPIO_InitPin(GPIO_PIN_0 | GPIO_PORT_A | GPIO_ALTER | GPIO_PULLUP | GPIO_AF1); // TIM2_CH1
|
|
GPIO_InitPin(GPIO_PIN_1 | GPIO_PORT_A | GPIO_ALTER | GPIO_PULLUP | GPIO_AF1); // TIM2_CH2
|
|
GPIO_InitPin(GPIO_PIN_10 | GPIO_PORT_A | GPIO_ALTER | GPIO_PULLUP | GPIO_AF6); // TIM1_CH3
|
|
GPIO_InitPin(GPIO_PIN_11 | GPIO_PORT_A | GPIO_ALTER | GPIO_PULLUP | GPIO_AF11); // TIM1_CH4
|
|
|
|
DSHOT_RX_Port=GPIOA;
|
|
DSHOT_TX_Mode=GPIO_MODER_MODE0_1 | GPIO_MODER_MODE1_1 | GPIO_MODER_MODE10_1 | GPIO_MODER_MODE11_1;
|
|
DSHOT_RX_Mode=~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1 | GPIO_MODER_MODE10 | GPIO_MODER_MODE11);
|
|
|
|
DSHOT_RX_Index[0]=1<<GPIO_PIN_0;
|
|
DSHOT_RX_Index[1]=1<<GPIO_PIN_1;
|
|
DSHOT_RX_Index[2]=1<<GPIO_PIN_10;
|
|
DSHOT_RX_Index[3]=1<<GPIO_PIN_11;
|
|
|
|
DSHOT_Mode(Freq, Bidirect);
|
|
|
|
for (int a = 0; a < DSHOT_TIM; a++) // Zero end in buffer
|
|
for (int b = DSHOT_FRAME_DATA*DSHOT_CCR; b < DSHOT_FRAME_SIZE*DSHOT_CCR; b++)
|
|
DSHOT_TX_Buff[a][b] = 0;
|
|
|
|
if(Bidirect) Init_ModeRX();
|
|
|
|
Init_TIM2_TX();
|
|
Init_TIM1_TX();
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void ds_packet1(unsigned short throttle, bool tele, unsigned short* buff)
|
|
{
|
|
unsigned short packet = (throttle << 5) | ((tele) << 4);
|
|
unsigned short checksum;
|
|
|
|
if(DSHOT_Bidirect) checksum = ~(packet >> 4 ^ packet >> 8 ^ packet >> 12) & 0x0F;
|
|
else checksum = (packet >> 4 ^ packet >> 8 ^ packet >> 12) & 0x0F;
|
|
|
|
packet |= checksum;
|
|
|
|
for (int a = 0; a < DSHOT_FRAME_DATA; a++) buff[a] = (packet & (0x8000 >> a)) ? DSHOT_Bit_1 : DSHOT_Bit_0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void ds_packet2(unsigned short throttle, bool tele, unsigned short* buff, unsigned long shift)
|
|
{
|
|
unsigned short packet = (throttle << 5) | ((tele) << 4);
|
|
unsigned short checksum;
|
|
|
|
if(DSHOT_Bidirect) checksum = ~(packet >> 4 ^ packet >> 8 ^ packet >> 12) & 0x0F;
|
|
else checksum = (packet >> 4 ^ packet >> 8 ^ packet >> 12) & 0x0F;
|
|
|
|
packet |= checksum;
|
|
|
|
for (int a = 0, b = shift; a < DSHOT_FRAME_DATA; a++, b+=2) buff[b] = (packet & (0x8000 >> a)) ? DSHOT_Bit_1 : DSHOT_Bit_0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool DSHOT_SetCommand(unsigned short Command[4], bool Forced)
|
|
{
|
|
if(!Command) return false;
|
|
|
|
if(!Forced)
|
|
{
|
|
if(DMA2_Channel1->CNDTR || DMA2_Channel2->CNDTR) return false;
|
|
if(DSHOT_Bidirect && DMA2_Channel3->CNDTR) return false;
|
|
}
|
|
|
|
TIM2->CR1 = TIM1->CR1 = TIM6->CR1 = 0;
|
|
TIM2->DIER = TIM1->DIER = TIM6->DIER = 0;
|
|
|
|
if(Forced) Begin_ModeTX();
|
|
|
|
ds_packet2(Command[0] & 0x0FFF, Command[0] >> 15, DSHOT_TX_Buff[DSHOT_TIM2], DSHOT_CCR1);
|
|
ds_packet2(Command[1] & 0x0FFF, Command[1] >> 15, DSHOT_TX_Buff[DSHOT_TIM2], DSHOT_CCR2);
|
|
|
|
ds_packet2(Command[2] & 0x0FFF, Command[2] >> 15, DSHOT_TX_Buff[DSHOT_TIM1], DSHOT_CCR3);
|
|
ds_packet2(Command[3] & 0x0FFF, Command[3] >> 15, DSHOT_TX_Buff[DSHOT_TIM1], DSHOT_CCR4);
|
|
|
|
DMA2_Channel1->CCR &= ~DMA_CCR_EN;
|
|
DMA2_Channel1->CNDTR = DSHOT_FRAME_SIZE*DSHOT_CCR;
|
|
DMA2_Channel1->CCR |= DMA_CCR_EN;
|
|
|
|
DMA2_Channel2->CCR &= ~DMA_CCR_EN;
|
|
DMA2_Channel2->CNDTR = DSHOT_FRAME_SIZE*DSHOT_CCR;
|
|
DMA2_Channel2->CCR |= DMA_CCR_EN;
|
|
|
|
TIM2->DIER = TIM1->DIER = TIM_DIER_UDE;
|
|
|
|
if(DSHOT_Bidirect)
|
|
{
|
|
DMA2_Channel3->CCR &= ~DMA_CCR_EN;
|
|
DMA2_Channel3->CNDTR = DSHOT_RX_SIZE*DSHOT_RX_SAMP;
|
|
DMA2_Channel3->CCR |= DMA_CCR_EN;
|
|
}
|
|
|
|
TIM1->CR1 = TIM_CR1_CEN; // TIM1(master) & TIM2(slaver)
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool DSHOT_GetERPM(unsigned short eRPM[4], bool Error[4])
|
|
{
|
|
if(!eRPM) return false;
|
|
|
|
struct
|
|
{
|
|
bool prev=true;
|
|
unsigned long len=0;
|
|
unsigned long code=0;
|
|
unsigned long count=0;
|
|
}data[DSHOT_COUNT];
|
|
|
|
for(int a=1; a<DSHOT_RX_SIZE*DSHOT_RX_SAMP-1; a++)
|
|
{
|
|
unsigned short* buf = DSHOT_RX_Buff;
|
|
unsigned short filt = (buf[a-1] & buf[a]) | (buf[a] & buf[a+1]) | (buf[a-1] & buf[a+1]);
|
|
|
|
|
|
for(int b=0; b<DSHOT_COUNT; b++)
|
|
{
|
|
bool bit = filt & DSHOT_RX_Index[b];
|
|
bool prv = data[b].prev;
|
|
|
|
if(prv!=bit)
|
|
{
|
|
unsigned long cnt = data[b].len ? data[b].count : 0;
|
|
|
|
if(cnt>=DSHOT_RX_SAMP*3-2)
|
|
{
|
|
data[b].len+=3;
|
|
data[b].code<<=3;
|
|
if(prv) data[b].code|=0x07;
|
|
}
|
|
else if(cnt>=DSHOT_RX_SAMP*2-2)
|
|
{
|
|
data[b].len+=2;
|
|
data[b].code<<=2;
|
|
if(prv) data[b].code|=0x03;
|
|
}
|
|
else
|
|
{
|
|
data[b].len+=1;
|
|
data[b].code<<=1;
|
|
if(prv) data[b].code|=0x01;
|
|
}
|
|
|
|
data[b].prev=bit;
|
|
data[b].count=0;
|
|
}
|
|
else
|
|
{
|
|
data[b].count++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for(int a=0; a<4; a++)
|
|
{
|
|
if(data[a].len==21)
|
|
{
|
|
data[a].code<<=1;
|
|
data[a].code|=1;
|
|
}
|
|
else if(data[a].len==20)
|
|
{
|
|
data[a].code<<=2;
|
|
data[a].code|=3;
|
|
}
|
|
|
|
short erpm = CalcERPM(data[a].code);
|
|
if(Error)
|
|
{
|
|
Error[a]=erpm==-1;
|
|
if(Error[a] && !a)
|
|
int z=0;
|
|
}
|
|
eRPM[a] = erpm;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|