使用STM32 HAL库实现DMX512控制灯光

STM32通过HAL库实现DMX512控制灯

  • DMX512协议是什么
  • 实现DMX512协议
  • 通过串口模拟
  • 串口配置(通过cubmx):
  • 发送函数:
  • 一些问题
  • 为什么不用寄存器方式发
  • DMX512协议是什么

    关于DMX512协议的解释下面这篇文章写的十分详尽:
    链接:作者:夏沫の浅雨

    实现DMX512协议

    通过串口模拟

    通过上文我们可以得知,数据帧需要1位低电平+8位数据位+2位高电平,这种格式与串口通信协议的格式可以说是几乎一模一样,因为平日基本上都是使用串口8N1格式。因此,若是不考虑接收基于DMX512协议的数据,也就是本文这种情况,完全可以使用8位数据位+2位停止位的串口通信方式模拟DMX512协议。

    串口配置(通过cubmx):

    Alt
    为实现发送一位数据用4us,配置波特率250000bit/s,8位数据位,2位停止位,无校验。

    发送函数:

    由于break段与MAB段很长,串口不能模拟,所以得先把串口所用引脚复用给变成输出,去模拟:

    void UartPortModeChang(uint8_t mode){
    	
    	GPIO_InitTypeDef GPIO_InitStruct = {0};
    	
    	if(mode){
    		GPIO_InitStruct.Pin = GPIO_PIN_2;
    		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    		GPIO_InitStruct.Pull = GPIO_PULLUP;
    		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    	}else{
    		GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    	}
    }
    

    接下来就是发送数据了,us延时很多,随便使用一种方法都可以,本文使用定时器6产生:

    void DMX512_Send(uint8_t buff[],uint16_t num){//num为数据长度,因为有0X00所以不能用strlen计算长度
    	uint8_t delay_num = 80;
    	uint8_t send_buff[50] = {0};
    	for(uint8_t i = 0;i <= num;++i){
    		if(i == 0){
    			send_buff[0] = 0X00;
    		}else{
    			send_buff[i] = buff[i-1];
    		}
    	}
    	huart2.TxXferCount = num;
    	UartPortModeChang(1);
    	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
    	delay_us(88);
    	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);
    	while(delay_num--);
    	UartPortModeChang(0);
    	HAL_UART_Transmit(&huart2,send_buff,num+1,0xff);//此处为可以发0X00进行了一点修改
    }
    

    关于HAL串口发送函数的修改如下:

    	while (huart->TxXferCount > 0U)
        {
          if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
          {
            return HAL_TIMEOUT;
          }
    //      if (pdata8bits == NULL)
    //      {
    //        huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU);
    //        pdata16bits++;
    //      }
    //      else
    //      {
            huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU);
            pdata8bits++;
    //      }
          huart->TxXferCount--;
        }
    

    注释掉这个判断是因为NULL等于0X00,故而会转而发送pdata16bits,这个变量是在配置串口为9位数据位无校验时用于存储待发送数据的。至此就能通过这个函数发送数据控制灯了。

    一些问题

    为什么不用寄存器方式发

    一开始网上能找到的有限的教程都是寄存器方式发送,按照一般逻辑这种方式应该也是最快的,所以我也是照着写了一次拿逻辑分析仪看了看:
    Alt
    代码如下:

    void DMX512_Send(uint8_t buff[],uint16_t num){
    
    	uint8_t delay_num = 80;
    	uint16_t num_div = num;
    	huart2.TxXferCount = num;
    	huart2.gState = HAL_UART_STATE_BUSY_TX;
    	UartPortModeChang(1);
    	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
    	delay_us(88);
    	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);
    	while(delay_num--);
    	UartPortModeChang(0);
    	huart2.Instance->DR = 0X00;
    	while((huart2.Instance->SR & 0X40) == 0);
    	huart2.Instance->DR = buff[0];
    	while((huart2.Instance->SR & 0X40) == 0);
    	while (huart2.TxXferCount > 0U){
    		huart2.Instance->DR = *(++buff);
    		while((huart2.Instance->SR & 0X40) == 0);
    		huart2.TxXferCount--;
    	}
    	huart2.gState = HAL_UART_STATE_READY;
    }
    

    再看通过本文写的方法模拟的DMX512协议发送同样的数据通过逻辑分析仪获取效果:
    Alt
    相信区别是很明显的,虽然实际使用起来没什么区别,但是就是看着别扭,至于为什么多一个位高电平我也不清楚,若是有大佬能解释还请告知,不胜感激!

    作者:热爱学习的任同学

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32 HAL库实现DMX512控制灯光

    发表回复