Hello there,
I am trying to control 2 LED strips (one with a WS2811 LED driver and one with a USC512C4 LED driver) with my STM32L4 with integrated RS485.
Do you have any idea how to do this?
Best regard.
P.Congré
Hello there,
I am trying to control 2 LED strips (one with a WS2811 LED driver and one with a USC512C4 LED driver) with my STM32L4 with integrated RS485.
Do you have any idea how to do this?
Best regard.
P.Congré
I should have added that there's also useful material about DMX512 here on the community. Here are a couple of good examples from shabaz and from genebren
DMX Explained; DMX512 and RS-485 Protocol Detail for Lighting Applications
DMX diagnostic tool - Getting started
DMX diagnostic tool - Sending and Receiving Data
Hello Jon,
I managed to control my LED strip with the USC512C4 IC with my arduino, but I need to do it with my STM32L4 but I can't find anywhere how to do it.
Do you have a code example for an STM32L4?
Best regard.
P.Congré
Sorry, I don't have any code I can give you.
Can't you port what you're doing with the Arduino to the STM32L4? It shouldn't be too difficult to send serial characters, so the tricky bit will be the break as that depends very much on what the particular serial peripheral can do. It might allow you to do it directly. Or you might be able to see a way to do it indirectly (by, for instance, enabling and disabling the peripheral so that it moves back to being an IO pin for a time, or by flipping the sense of the output pin if it can be changed from positive true to negative true). If you can't see a way to do it in the processor, you could always add an external logic gate to switch between serial and the break state. You'll probably need to use a timer to get the break period, so you'll have to work out how to do that too (I'm sure there's plenty of material on how to do that kind of thing).
Hello Jon,
There is no communication between the arduino and the STM32L4, and i can't port yhe arduino code to the stm32.
With the arduino, I managed to control my LED strip with DMX protocol (the LED strip has a USC512C4 IC).
Now I want to control the LED strip with my STM32L4 but it is quite difficult.
I use an RS-485 to create the DMX communication. I found some codes on the internet but it doesn't work.
https://github.com/aleksandrgilfanov/stm32f4-dmx-transmitter/blob/master/Src/main.c
https://github.com/carl3721/stm32-dmx512/blob/master/dmx-p103/src/main.c
Best regard.
P.Congré
The project at the first link you give appears to be based on some modified ST code. Perhaps you could ask ST for the original code if you can't find it on their website. It's very unlikely they would issue anything that didn't work.
Is there a reason why you can't just use the Arduino, since you have that working?
I already tried to contact ST, but nothing for the moment.
I can't use arduino because the next step of the project is to control the LED strip with an STM32.
best regard.
P.Congré
If you don't mind a hardware engineer telling you how to deal with debugging, here's how I'd tackle it:
Does anything at all emerge from the STM32? Can you see any sign of serial waveforms at the output pin, even if it just sends a frame of 513 characters and then freezes at the break?
Is your 485 chip matched to the IO voltage standard of the processor? [Not all of the older ones would be ok on 3.3V]
What can you see on the A and B outputs of the driver chip?
Have you wired the outputs the right way round to the LED strip?
If you see a lot of overshoot and the sigals are very messy, look carefully at how you've done the termination on the RS-485. [The strip might be much more fussy than a normal DMX receiver.]
If there's still nothing, try hacking together a bit of very simple test code to repetitively send a serial character and look again.
If that works, go back to the DMX code and disable the stuff that does the break [the fiddly stuff with a timer that could esily go wrong] and verify that it's sending the character stream ok, then start working on the break code, maybe even isolating it and just repetitively sending the break until you get the timing right.
I can see the waveform of my TX PIN when I send 513 times a "U".
The 485 chip matches the IO voltage, I checked it and I can also see the waveform of the A and B output.
I double checked the cable connection for the LED strip.
I know it's not hardware but rather software, I couldn't create a correct code to control the LEDs.
Best regard.
P.Congré
^ Great debugging tips from Jon.
Right now I'm trying to get hardware and software working. I did not create the hardware nor the software, but I won't raise a case until I've:
1. Verified connections and voltages
2. Connected a 'scope or attached a logic analyzer (they cost less than $20) and examined if the interface signals are what I expect
3. Grabbed debug from the code
4. Tried different hardware and software combinations to try to narrow down the problem
5. Sketch a diagram or take photos or video to show precisely what I'm doing
6. Supply any configurations or log file content
For your scenario, maybe point 2 and 4 are also relevant, since you could compare the working scenario (Arduino) with the non-working scenario, and compare the signals, to see what the difference is.
Here are a couple of things to check:
Good luck!
That's good. You're not far off a working system.
Do you see the break, after the frame of data is sent, present in the DMX waveform?
I've never used the devices on the LED strip, but they probably depend on the subsequent break as part of their operation.
It's also part of the DMX spec that you have to keep sending the break and the frame repetitively. I don't think the spec says what the receiver should do if the DMX disappears, but I think it implies the designer of the receiver should do something for what is then considered to be a fault condition if it disappears for more than a second or two [for a lamp in a theatre setting, the normal thing would be for it to shut down on loss of DMX to stop it interferring with the rest of a lighting rig].
The problem is that I don't really know what line to follow to create the code, I know I need to set up a uart communication with 250000 bauderate.
I know the DMX protocol but I don't know how to implement it in my stm32.
The code for the arduino is an example that I modified to create different animations.
best regard.
P.Congré
The problem is that I don't really know what line to follow to create the code, I know I need to set up a uart communication with 250000 bauderate.
I know the DMX protocol but I don't know how to implement it in my stm32.
The code for the arduino is an example that I modified to create different animations.
best regard.
P.Congré
Paul,
I do not have much (if any) experience with STM32, but I did find this code that might help you along:
https://github.com/carl3721/stm32-dmx512/blob/master/dmx-p103/src/main.c
It was an easy search of "stm32 dmx example", which seem to have lots of results.
I take from all of this that is very new to you. I have found that Google is your friend, and you will likely find lots of help there (and here). You really need to break it down into little pieces when you are looking for help. DMX has been around for quite a while, but there are not many examples, as a lot of people are buying solutions from a small handful of suppliers, that wrap all of the messy stuff up into libraries or standalone hardware.
Gene
Hello Gene,
I've already tried this git repository, it's only used to receive data, I'm trying to send data to the control light.
Best regard.
P.Congré
The two links to code that you posted are both code bases for STM32F10x processors - you are using an STM32L4xxx which will be different.
I can't guess all the things (if any) which will be wrong without:
1) Your code
2) A schematic or detailed block diagram of your set up
3) A pin list showing which processor pins are used for what
It would also help if you told us what software and debugging tools you are using (including do you have a scope or logic analyser).
Pictures often help as well.
MK
Good point Michael!
In addition it would be helpful to understand your level of understanding with coding on the STM32 products (and/or coding in general). If you are looking for code that you can just drop in, your are not likely to find a perfect solution. If you have the proper experience you should be able to find all you need to know from the documentation available from STM to take the concepts that you used on the Arduino and port them to the STM part.
genebren wrote:
... If you have the proper experience you should be able to find all you need to know from the documentation available from STM to take the concepts that you used on the Arduino and port them to the STM part.
Porting existing knowing-to-work code to the platform you're learning is an excellent way to get the experience.
You have the luxury to see everything working and know that there's no defects. When stuck, you can always go back to the working example and compare.
I've written a blog here on my approach to port a library between platforms. I'll search for it ...
edit: found: Educational BoosterPack and Hercules LaunchPad: LCD Driver - Part 3: SPI Works
Hello Gene
1) my code :
DMX.c
/*
* dmx.c
*
* Created on: 8 nov. 2021
* Author: Paul_C
*/
#include "main.h"
#include "dmx.h"
extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim15;
volatile static uint16_t slots_sent;
volatile static uint16_t slots_count;
volatile static const uint8_t *slots_ptr;
void dmx_send(const uint8_t *slots, uint16_t size)
{
/* Setup global variables, they will be accessed from interrupts */
slots_ptr = slots;
slots_sent = 0;
slots_count = size;
/*
* DMX512 packet starts with Reset Sequence. Reset Sequence starts with
* Break (low level), so set pin to output low.
*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
/*
* Waiting for Break time, and everything further, is done through TIM3
* interrupt. At first interrupt (CC1) this timer changes pin to high
* for MarkAfterBreak. Then, in update interrupt, it configures pin into
* floating mode. Right after this, it sends first DMX byte 0x00, and
* turn on second timer for sending slots after MarkBetween slots.
*/
/* So, enable this timer */
__HAL_TIM_ENABLE(&htim15);
/* Wait until transmission of whole DMX packet is finished */
while (slots_ptr != NULL) {};
}
/* Interrupt handler for sending next slot (from TIM2 interrupt) */
void dmx_slot(void)
{
/*
* Each slot is sent in Update Interrupt of TIM2. So, period of TIM2 is
* time, needed for 11bits of slot (44us) and Mark Between Slots (0..1s)
*/
if (slots_sent < slots_count)
USART1->TDR = slots_ptr[slots_sent++];
else {
/* Stop timer when all slots are sent */
TIM2->CR1 &= ~(TIM_CR1_CEN);
TIM2->CNT = 0;
/* Indicate that transmission finished */
slots_ptr = NULL;
}
}
/* Interrupt handler for sending reset sequence (from TIM3 interrupt) */
void dmx_reset_sequence(void)
{
if (TIM15->SR & TIM_IT_UPDATE)
{
/* Reset sequence finished, stop timer */
TIM15->CR1 &= ~(TIM_CR1_CEN);
TIM15->CNT = 0;
/* Send start code 0x00 */
USART1->TDR = 0x00;
/* Start timer for sending slots */
__HAL_TIM_ENABLE(&htim2);
}
else if (TIM15->SR & TIM_IT_CC1)
{
/* Break end, set floating ping mode for Mark After Break */
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
}
DMX.h
#ifndef INC_DMX_H_
#define INC_DMX_H_
#define DMX_BREAK 92
#define DMX_MAB 12
#define DMX_SLOT 44
#define DMX_MBS 0
void dmx_send(const uint8_t *slots, uint16_t size);
void dmx_slot(void);
void dmx_reset_sequence(void);
main.c
the first main i done
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "dmx.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//#define USEC_COUNT (95/2)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim15;
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM15_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int i;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
MX_TIM15_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(0.080);
HAL_UART_Transmit(&huart1, 0x00, 100, 100);
for(i = 0; i <= 513; i++){
HAL_UART_Transmit(&huart1, 255, 100, 100);
i++;
}
HAL_UART_Transmit(&huart1, 0xFF, 100, 100);
i = 0;
HAL_Delay(0.012);
HAL_Delay(200);
}
/*if(dmx_sent) {
dmx_sent = 0;
packetizer_tick(&uasrt1_pactetizer);*/
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief TIM15 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM15_Init(void)
{
/* USER CODE BEGIN TIM15_Init 0 */
/* USER CODE END TIM15_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM15_Init 1 */
/* USER CODE END TIM15_Init 1 */
htim15.Instance = TIM15;
htim15.Init.Prescaler = 0;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 65535;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim15) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM15_Init 2 */
/* USER CODE END TIM15_Init 2 */
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 250000;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_2;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_RS485Ex_Init(&huart1, UART_DE_POLARITY_HIGH, 0, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, SMPS_EN_Pin|SMPS_V1_Pin|SMPS_SW_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : SMPS_EN_Pin SMPS_V1_Pin SMPS_SW_Pin */
GPIO_InitStruct.Pin = SMPS_EN_Pin|SMPS_V1_Pin|SMPS_SW_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : SMPS_PG_Pin */
GPIO_InitStruct.Pin = SMPS_PG_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SMPS_PG_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD4_Pin */
GPIO_InitStruct.Pin = LD4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD4_GPIO_Port, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
the second one
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "dmx.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define USEC_COUNT (95/2)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim15;
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM15_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int i;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
MX_TIM15_Init();
/* USER CODE BEGIN 2 */
uint8_t test_packet[512];
for (int i=0; i<sizeof(test_packet); i++)
test_packet[i] = i & 0xFF;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
dmx_send(test_packet, sizeof(test_packet));
HAL_Delay(1000);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief TIM15 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM15_Init(void)
{
/* USER CODE BEGIN TIM15_Init 0 */
/* USER CODE END TIM15_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM15_Init 1 */
/* USER CODE END TIM15_Init 1 */
htim15.Instance = TIM15;
htim15.Init.Prescaler = 0;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 65535;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim15) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM15_Init 2 */
/* USER CODE END TIM15_Init 2 */
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 250000;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_2;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_RS485Ex_Init(&huart1, UART_DE_POLARITY_HIGH, 0, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, SMPS_EN_Pin|SMPS_V1_Pin|SMPS_SW_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : SMPS_EN_Pin SMPS_V1_Pin SMPS_SW_Pin */
GPIO_InitStruct.Pin = SMPS_EN_Pin|SMPS_V1_Pin|SMPS_SW_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : SMPS_PG_Pin */
GPIO_InitStruct.Pin = SMPS_PG_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SMPS_PG_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD4_Pin */
GPIO_InitStruct.Pin = LD4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD4_GPIO_Port, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
2) There is a diagram block of the instalation
3) i use the USART1 of my STM32L412 on the pin PA10 for the RX and PA9 for the TX
i also use 2 timer, timer2 internal clock input capture direct mode and the timer15 with the same configuration
To do that i use the ST's software, CUBEide and CUBEMX
P.Congré
Hello Paul,
OK, I see your code, and I think I understand what you are trying to do.
(For future reference, if you want to share some code the best way is to zip up the top level folder of the complete project (with all the cube files etc) and transfer it by Dropbox or whatever.
That way I could just run it - to get your code into a compileable form would take an hour or two - and that's a bit too long for me to do.)
A long time ago (2013) I wrote code for a DMX controller and a DMC controlled device.
void tx_dmx(uint8_t *data, uint32_t n) { // Timing according to DMX spec // 1 "SPACE" for BREAK - flexible, depends on time between bursts of data, min allowed 92uS // 2 "MARK" after break - min allowed is 12uS, we use 25uS; // 9 "MARK" between slots - min allowed is zero, we use 10uS // 10 "MARK" before break, min allowed is zer, we use 10uS // // we usually only send < 32 bytes so the total time taken is about 25 + (32 * ((11 * 4) + 10)) + 10 = 1763 uS // full length DMX frame would take 27737 uS which is OK occasionally but not good full time. // no need to use interrupts, we just block int d; if (n > DMX_MAX_TX) { n = DMX_MAX_TX; } GPIOA->MODER = (GPIOA->MODER & ~(3 << 18)) | (2 << 18); // set mode bits for A9 to 10 for UART // ends the break and starts the mark TIM2->CNT = 0; while (TIM2->CNT < MARK_AFTER_BREAK) {} for(d = 0; d < n; d++) { USART1->TDR = *data++; TIM2->CNT = 0; while (TIM2->CNT < MARK_BETWEEN_SLOTS + BYTE_TIME) {} } GPIOA->MODER = (GPIOA->MODER & ~(3 << 18)) | (1 << 18); // set mode bits for A9 to 01 for GPIO - DMX BREAK }
It blocks rather than use interrupts - not so slick but much easier to debug (because you can step through it one instruction at a time and measure pins with a DMM if that's all you have.)
I can post more of it if useful.
You will need a debugger (and to to do embedded code development you NEED a debugging tool). The ST Nucleo boards all have one built in. If you have your own board you can buy an ST Link for less than £20.
You will notice that I don't use the ST library code to controll USART and GPIO.
It might take a little longer to write the code at first but it does mean that you have to understand the peripherals - which is worth the hassle.
On a new project, especially if using using a new (to me) processor I usually put a "Blinky" and a hello world in the code.
SO it starts by blinking an LED (that tells me that the processor is running at the right speed) and uses the debug UART (there should always be a debug UART in any project where the processor has more than 28 pins)
to say "Project XX Build YY".
You might want to try these ideas on your project,
1) Get an LED blinking (so you can check timing - I've had a 200MHz uP almost working at 240MHz, took a long time to work out why the IO was flaky but nearly working)
2) Get used to looking at the peripheral registers with the debugger
3) Write your code so it is easy to step through
4) Expect to change code as you get stuff working, don't use interrupts at first unless you really have to, write blocking code, get it working, then apply the DMA and interrupts (and only if necessary).
5) (controversial) never trust the processor manufacturers library code, still less trust code you got free from the web, it can be very useful for getting ideas, learning stuff and so on - just don't count on it being good.
MK
Hello Michael,
I am using a nucleo-l412-rb-p for debugging.
I will try not to use the st library, it would be interesting for me if you could send more code examples.
P.Congré
Hello Paul,
I wouldn't change your code just yet, try stepping through and checking the peripheral settings as you go.
The ST GPIO stuff usually works OK but it's often slow - but that won't hurt too much at first.
I'll post a link to the project later.
MK
Dropbox Link:
https://www.dropbox.com/sh/u9vejrf0vm2fn1x/AAAaACwr017fXwtWkPWe9oC2a?dl=0
I won't leave it up for long, so please let me know when you download it.
It's a complete Keil MDK project, with a lot of stuff that you don't need and which will make little sense.
Concentrate on the dmx files.
I see that in this project I use the ST lib functions to set up the timer (wouldn't do that now) and I use the ST touch support library (which I had a few problems with).
You can run it in the Keil toolset (free sub 32k version) which is a big but zero cost download from www.keil.com
You will need to install the legacy support stuff as well (search legacy support on Keil website)
MK