EasyScale单总线数字调光

一、协议介绍

上篇文章【STM32】基于TPS61165芯片的LED驱动电路-CSDN博客介绍了TPS61165芯片的传统操作(PWM调光和高低电平点亮熄灭)。看到网友留言芯片还可以用单总线控制,便有了这篇文章。

芯片有2种调光模式,一种就是之前使用的PWM调光,还有一种叫做EasyScale 单总线数字调光。

芯片默认是PWM调光,要想进入单总线模式,需要在芯片从shutdown起来后执行以下3个步骤

对应的时序图如下

其中关键的时序参数如下表格所示

可以看出,逻辑0是长低+短高,逻辑1是短低+长高。下降沿开始,下降沿结束。

笔者设置逻辑0是300us的低电平+100us的高电平;逻辑1是100us的低电平+300us的高电平。

us延时函数

cpp 复制代码
#define CPU_FREQUENCY_MHZ 72 // STM32时钟主频
void delay_us(__IO uint32_t delay) {
   int last, curr, val;
   int temp;
   while (delay != 0) {
       temp = delay > 900 ? 900 : delay;
       last = SysTick->VAL;
       curr = last - CPU_FREQUENCY_MHZ * temp;
       if (curr >= 0) {
           do {
               val = SysTick->VAL;
           } while ((val < last) && (val >= curr));
       } else {
           curr += CPU_FREQUENCY_MHZ * 1000;
           do {
               val = SysTick->VAL;
           } while ((val <= last) || (val > curr));
       }
       delay -= temp;
   }
}

逻辑生成函数

cpp 复制代码
/* short:100us long:300us */
static void genLogic(uint8_t logic)
{
   if(logic == 0)
   {
      CTRL_LOW();
      delay_us(300);
      CTRL_HIGH();
      delay_us(100);
   }
   else if(logic == 1)
   {
      CTRL_LOW();
      delay_us(100);
      CTRL_HIGH();
      delay_us(300);
   }
   else
   {

   }
}

先拉低CTRL引脚至少2.5ms让芯片进入shutdown

cpp 复制代码
void LED_Shutdown()
{
   CTRL_LOW();
   HAL_Delay(3);
}

拉高CTRL,保持至少100us,再拉低至少260us,拉高CTRL。至此,芯片就成功进入单总线数字调光模式了。从CTRL被拉高开始,至少1ms后(检测延时>100us,检测时间>260us)再发送编程波形。笔者简化处理为CTRL拉高600us,CTRL拉低600us,再拉高

cpp 复制代码
void LED_Enter()
{
   CTRL_HIGH();
   delay_us(600);
   CTRL_LOW();
   delay_us(600);
   CTRL_HIGH();
}

一旦设置好亮度,就不能在线修改,需要将芯片先shutdown,唤醒后再发送数据调节亮度。

这里阐述了这个功能的应用场景:有时其实我们并不需要一直给芯片发PWM,可以让芯片通过单总线配置好亮度就进入空闲状态(低功耗/休眠)以节约能源消耗。

单总线数字调光是在总线上发送不同的数据来改变芯片反馈引脚的电压实现调光,有32个挡位,如下表格所示

二、协议实现

数据链路

有点类似I2C总线。地址固定为0x72。一帧信息为起始+设备地址+结束+起始+数据+结束。

发送字节前需要发送Start

cpp 复制代码
static void writeStart()
{
   CTRL_HIGH();
   delay_us(50);
}

发送数据

cpp 复制代码
static void writeData(uint8_t dat)
{
   for (uint8_t i = 0; i < 8; i++)
   {
      genLogic(((dat << i) & 0x80) == 0x80);
   }
}

发送完一个字节需要发送EOS

cpp 复制代码
static void writeEOS()
{
   CTRL_LOW();
   delay_us(50);
   CTRL_HIGH();
}

如果需要响应,切换IO模式

cpp 复制代码
static void changePinMode()
{
   GPIO_InitTypeDef GPIO_InitStruct = {0};

  /*Configure GPIO pin : CTRL_Pin */
  GPIO_InitStruct.Pin = CTRL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(CTRL_GPIO_Port, &GPIO_InitStruct);
}

static void resumePinMode()
{
   GPIO_InitTypeDef GPIO_InitStruct = {0};

  /*Configure GPIO pin : CTRL_Pin */
  GPIO_InitStruct.Pin = CTRL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(CTRL_GPIO_Port, &GPIO_InitStruct);
}

设置亮度函数

数据字节的最高位用于配置是否要芯片做出应答。

cpp 复制代码
HAL_StatusTypeDef LED_SetLight(uint8_t step)
{
   writeStart();
   writeData(0x72);
   writeEOS();

   writeStart();
#ifdef LED_ACK
   writeData(step | 0x80);
   writeEOS();
   changePinMode();
   if (HAL_GPIO_ReadPin(CTRL_GPIO_Port, CTRL_Pin) == GPIO_PIN_RESET)
   {
      resumePinMode();
      return HAL_OK;
   }
   else
   {
      resumePinMode();
      return HAL_ERROR;
   }
#else
   writeData(step);
   writeEOS();
   return HAL_OK;
#endif
}

无需响应的情形

发送完地址的最后一个bit,等待一段时间EOS(小于360us),拉高CTRL(Tstart),开始发送数据字节,发送完毕后等待EOS,总线常高。连接CTRL的单片机引脚配置推挽输出。

使用逻辑分析仪采集波形如下

需响应的情形

数据字节最高位是1,发送完数据字节,等待EOS时间,如果芯片应答,CTRL引脚会被芯片拉低一段时间tACK(<512us),需要在总线配置上拉电阻用于检测CTRL电平判断是否正确应答(拉低)。连接CTRL的单片机引脚配置开漏输出,并在CTRL引脚上拉电阻到3.3V。

发送完数据后,改成输入模式,此时如果芯片正确响应,会把CTRL拉低,让MCU检测到就可以。检测完毕,将GPIO再切换回开漏输出。使用逻辑分析仪采集波形如下

设置好亮度后MCU就可以进低功耗模式。主函数如下

cpp 复制代码
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int32_t light = 0;
uint8_t str[20];
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* 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_DMA_Init();
  MX_SPI2_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
   KeyDrv_Config();
   ST7789_Init();
   ST7789_Clear(BLACK);
   __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
   uint8_t re = 0;
   LED_Shutdown();
   LED_Enter();
   re = LED_SetLight(light % 32);
   
   sprintf(str, "light:%d %d  ", light % 32, re);
   ST7789_ShowString(0, 140, str, Font_16x26, ST7789_RGB565(0xFFFF), BLACK);
   
   light+=3;
   HAL_Delay(1000);

   if (light == 30)
   {
      sprintf(str, "in stop mode");
      ST7789_ShowString(0, 140, str, Font_16x26, ST7789_RGB565(0xFFFF), BLACK);
      HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFE);
   }
   
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

三、驱动附录

LED.c

cpp 复制代码
#include "LED.h"

#define CPU_FREQUENCY_MHZ 72 // STM32时钟主频
void delay_us(__IO uint32_t delay) {
   int last, curr, val;
   int temp;
   while (delay != 0) {
       temp = delay > 900 ? 900 : delay;
       last = SysTick->VAL;
       curr = last - CPU_FREQUENCY_MHZ * temp;
       if (curr >= 0) {
           do {
               val = SysTick->VAL;
           } while ((val < last) && (val >= curr));
       } else {
           curr += CPU_FREQUENCY_MHZ * 1000;
           do {
               val = SysTick->VAL;
           } while ((val <= last) || (val > curr));
       }
       delay -= temp;
   }
}


/* short:100us long:300us */
static void genLogic(uint8_t logic)
{
   if(logic == 0)
   {
      CTRL_LOW();
      delay_us(300);
      CTRL_HIGH();
      delay_us(100);
   }
   else if(logic == 1)
   {
      CTRL_LOW();
      delay_us(100);
      CTRL_HIGH();
      delay_us(300);
   }
   else
   {

   }
}

static void writeData(uint8_t dat)
{
   for (uint8_t i = 0; i < 8; i++)
   {
      genLogic(((dat << i) & 0x80) == 0x80);
   }
}


static void writeStart()
{
   CTRL_HIGH();
   delay_us(50);
}

static void changePinMode()
{
   GPIO_InitTypeDef GPIO_InitStruct = {0};

  /*Configure GPIO pin : CTRL_Pin */
  GPIO_InitStruct.Pin = CTRL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(CTRL_GPIO_Port, &GPIO_InitStruct);
}

static void resumePinMode()
{
   GPIO_InitTypeDef GPIO_InitStruct = {0};

  /*Configure GPIO pin : CTRL_Pin */
  GPIO_InitStruct.Pin = CTRL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(CTRL_GPIO_Port, &GPIO_InitStruct);
}

static void writeEOS()
{
   CTRL_LOW();
   delay_us(50);
   CTRL_HIGH();
}


HAL_StatusTypeDef LED_SetLight(uint8_t step)
{
   writeStart();
   writeData(0x72);
   writeEOS();

   writeStart();
#ifdef LED_ACK
   writeData(step | 0x80);
   writeEOS();
   changePinMode();
   if (HAL_GPIO_ReadPin(CTRL_GPIO_Port, CTRL_Pin) == GPIO_PIN_RESET)
   {
      resumePinMode();
      return HAL_OK;
   }
   else
   {
      resumePinMode();
      return HAL_ERROR;
   }
#else
   writeData(step);
   writeEOS();
   return HAL_OK;
#endif
}


void LED_Shutdown()
{
   CTRL_LOW();
   HAL_Delay(3);
}

void LED_Enter()
{
   CTRL_HIGH();
   delay_us(600);
   CTRL_LOW();
   delay_us(600);
   CTRL_HIGH();
}

LED.h

cpp 复制代码
#ifndef LED_H
#define LED_H

#include "GPIO.h"
#include "main.h"

// #define LED_ACK

#define CTRL_LOW()   HAL_GPIO_WritePin(CTRL_GPIO_Port, CTRL_Pin, GPIO_PIN_RESET)
#define CTRL_HIGH()   HAL_GPIO_WritePin(CTRL_GPIO_Port, CTRL_Pin, GPIO_PIN_SET)


HAL_StatusTypeDef LED_SetLight(uint8_t step);
void LED_Shutdown();
void LED_Enter();


#endif
相关推荐
驴友花雕20 分钟前
【花雕学编程】Arduino BLDC 之群体机器人协同探索
c++·单片机·嵌入式硬件·arduino bldc·群体机器人协同探索
驴友花雕23 分钟前
【花雕学编程】Arduino BLDC 之仿人机器人膝关节稳定系统
c++·单片机·嵌入式硬件·arduino bldc·仿人机器人膝关节稳定系统
linweidong6 小时前
嵌入式电机:如何在低速和高负载状态下保持FOC(Field-Oriented Control)算法的电流控制稳定?
stm32·单片机·算法
Zero_Era6 小时前
高性价比安全MCU——LKT6830C
单片机·嵌入式硬件·安全
国科安芯7 小时前
卫星通讯导航FPGA供电单元DCDC芯片ASP4644S2B可靠性分析
单片机·嵌入式硬件·fpga开发·架构·安全性测试
易水寒陈9 小时前
单片机实现的工厂模式
单片机
别了,李亚普诺夫10 小时前
PCB设计大师篇笔记
笔记·嵌入式硬件·学习
点灯小铭10 小时前
双积分AD转换电路
单片机·毕业设计·课程设计·期末大作业
youcans_11 小时前
【动手学STM32G4】(8)STM32G431之 DAC进阶
stm32·单片机·嵌入式硬件·dma·定时器
hqwest11 小时前
码上通QT实战14--监控页面06-串口设备数据初始化-
单片机·rs232·serialport·波特率·串口设备·虚拟串口设备软件·qt项目配置文件