Использование передачи таймера STM32-> GPIO для обработки данных синхронизации светодиодов WS2812B - PullRequest
0 голосов
/ 28 сентября 2018

Я работал над контроллером освещения, но мне не повезло в передаче данных через GPIO.Я использую библиотеку STM32F303VCT6 и HAL.В качестве эталона я использовал библиотеку WS2812B для STM32F3 от Hubmartin, модифицировав ее для обработки передачи данных синхронизации светодиодов в одной передаче DMA, в отличие от данных синхронизации битовой полосы в буфер длины двух светодиодов через круговой DMA.

Мой план состоял в том, чтобы использовать DMA для запуска каждого GPIO, для которого установлен светодиодный выход, для вывода «high» в случае обновления таймера.Затем по событию CC1 записать данные выводы в «низкий» через данные буфера.И затем на событии CC2 установить все контакты «низко».Это должно позволить мне обрабатывать данные синхронизации для нескольких полос параллельно.Вот моя реализация:

Чтобы инициализировать таймер:

static void TIM2_init(void){
    __HAL_RCC_TIM2_CLK_ENABLE();

    tim_period =  SystemCoreClock / 800000;
    uint32_t cc1 = (10 * tim_period) / 36;
    uint32_t cc2 = (10 * tim_period) / 15;

    Tim2Handle.Instance = TIM2;
    Tim2Handle.Init.Period            = tim_period;
    Tim2Handle.Init.RepetitionCounter = 0;
    Tim2Handle.Init.Prescaler         = 0;
    Tim2Handle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    Tim2Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;
    HAL_TIM_PWM_Init(&Tim2Handle);

    tim2OC1.OCMode       = TIM_OCMODE_PWM1;
    tim2OC1.OCPolarity   = TIM_OCPOLARITY_HIGH;
    tim2OC1.Pulse        = cc1;
    tim2OC1.OCFastMode   = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&Tim2Handle, &tim2OC1, TIM_CHANNEL_1);

    tim2OC2.Pulse        = cc2;
    HAL_TIM_PWM_ConfigChannel(&Tim2Handle, &tim2OC2, TIM_CHANNEL_2);
}

Чтобы инициализировать DMA:

static void DMA_init(void){
    __HAL_RCC_DMA1_CLK_ENABLE();

    dmaUpdate.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dmaUpdate.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaUpdate.Init.MemInc = DMA_MINC_DISABLE;
    dmaUpdate.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dmaUpdate.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    dmaUpdate.Init.Mode = DMA_NORMAL;
    dmaUpdate.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dmaUpdate.Instance = DMA1_Channel2;
    HAL_DMA_Init(&dmaUpdate);
    __HAL_LINKDMA(&Tim2Handle, hdma[TIM_DMA_ID_UPDATE], dmaUpdate);

    dmaCC1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dmaCC1.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaCC1.Init.MemInc = DMA_MINC_ENABLE;
    dmaCC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dmaCC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    dmaCC1.Init.Mode = DMA_NORMAL;
    dmaCC1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dmaCC1.Instance = DMA1_Channel5;
    HAL_DMA_Init(&dmaCC1);
    __HAL_LINKDMA(&Tim2Handle, hdma[TIM_DMA_ID_CC1], dmaCC1);

    dmaCC2.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dmaCC2.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaCC2.Init.MemInc = DMA_MINC_DISABLE;
    dmaCC2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dmaCC2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    dmaCC2.Init.Mode = DMA_NORMAL;
    dmaCC2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dmaCC2.Instance = DMA1_Channel7;
    HAL_DMA_Init(&dmaCC2);
    __HAL_LINKDMA(&Tim2Handle, hdma[TIM_DMA_ID_CC2], dmaCC2);

    dmaCC2.XferCpltCallback  = DMA_TransferCompleteHandler;
    HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}

Чтобы начать передачу:

static void WS2812_sendbuf(){
    ws2812b.transferComplete = 0;

    HAL_DMA_Start(&dmaUpdate,(uint32_t)WS2812_IO_High, (uint32_t)&WS2812B_PORT->BSRR, BUFFER_SIZE);
    HAL_DMA_Start(&dmaCC1,(uint32_t)ws2812bDmaBitBuffer, (uint32_t)&WS2812B_PORT->BRR, BUFFER_SIZE);
    HAL_DMA_Start_IT(&dmaCC2,(uint32_t)WS2812_IO_Low, (uint32_t)&WS2812B_PORT->BSRR, BUFFER_SIZE);
    __HAL_TIM_ENABLE_DMA(&Tim2Handle, TIM_DMA_UPDATE);
    __HAL_TIM_ENABLE_DMA(&Tim2Handle, TIM_DMA_CC1);
    __HAL_TIM_ENABLE_DMA(&Tim2Handle, TIM_DMA_CC2);

    __HAL_TIM_ENABLE(&Tim2Handle);
}

Для обратного вызова при завершении вывода DMA:

void DMA1_Channel7_IRQHandler(void){
    HAL_DMA_IRQHandler(&dmaCC2);
}

void DMA_TransferCompleteHandler(DMA_HandleTypeDef *DmaHandle){
    WS2812B_PORT->BSRR = WS2812_IO_Low[0];

    __HAL_TIM_DISABLE_DMA(&Tim2Handle, TIM_DMA_UPDATE);
    __HAL_TIM_DISABLE_DMA(&Tim2Handle, TIM_DMA_CC1);
    __HAL_TIM_DISABLE_DMA(&Tim2Handle, TIM_DMA_CC2);
    __HAL_TIM_DISABLE(&Tim2Handle);

    ws2812b.transferComplete = 1;
}

Хотя я могу убедиться, что начальная реализация работает, как и было задумано, в текущем драйвере HAL есть некоторое странное поведение, которое я пытаюсь преодолеть, имея все своиданные доступны до начала передачи данных о времени.

В моей текущей реализации я отправляю один буфер данных и начинаю передачу данных синхронизации, а затем задерживаю 100 миллисекунд, прежде чем снова начать процесс.Я смущен, обнаружив, что моя реализация вообще не отправляет какие-либо данные через GPIO, и я предполагаю, что допустил ошибки при запуске логики состояния / таймера.Кто-нибудь видит ошибку, которую я сделал, чтобы не выводить?

...