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
相关推荐
小龙报23 分钟前
【51单片机】从 0 到 1 玩转 51 蜂鸣器:分清有源无源,轻松驱动它奏响新年旋律
c语言·数据结构·c++·stm32·单片机·嵌入式硬件·51单片机
范纹杉想快点毕业24 分钟前
嵌入式与单片机开发核心学习指南——从思维转变到第一性原理的深度实践
单片机·嵌入式硬件
Industio_触觉智能28 分钟前
瑞芯微RK3566开发板规格书,详细参数配置,型号EVB3566-V1,基于RK3566核心板SOM3566邮票孔封装
嵌入式硬件·开发板·rk3568·rk3566·核心板·瑞芯微
czwxkn1 小时前
4STM32(stdl)TIM定时器
stm32·单片机·嵌入式硬件
Love Song残响1 小时前
NVIDIA显卡终极优化指南
stm32·单片机·嵌入式硬件
qq_672592751 小时前
电源芯片为什么发热
单片机·嵌入式硬件
天天爱吃肉82181 小时前
【跨界封神|周杰伦×王传福(陶晶莹主持):音乐创作与新能源NVH测试,底层逻辑竟完全同源!(新人必看入行指南)】
python·嵌入式硬件·算法·汽车
国科安芯2 小时前
抗辐照MCU在精密时频系统中的单粒子效应评估与可靠性验证
单片机·嵌入式硬件·架构·制造·安全性测试
森焱森2 小时前
嵌入式硬件工程师应知 白银快速分析报告
linux·c语言·arm开发·嵌入式硬件·去中心化
czhaii2 小时前
单片机最佳入门多线程示例讲解
c语言·单片机