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
相关推荐
Lester_110113 小时前
STM32 高级定时器PWM互补输出模式--如果没有死区,突然关闭PWM有产生瞬间导通的可能吗
stm32·单片机·嵌入式硬件·嵌入式软件
小李独爱秋14 小时前
“bootmgr is compressed”错误:根源、笔记本与台式机差异化解决方案深度指南
运维·stm32·单片机·嵌入式硬件·文件系统·电脑故障
梁洪飞16 小时前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm
进击的小头17 小时前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
宵时待雨20 小时前
STM32笔记归纳8:时钟
笔记·stm32·单片机·嵌入式硬件
JJRainbow21 小时前
SN75176 芯片设计RS-232 转 RS-485 通信模块设计原理图
stm32·单片机·嵌入式硬件·fpga开发·硬件工程
花月mmc21 小时前
CanMV K230 波形识别——整体部署(4)
人工智能·python·嵌入式硬件·深度学习·信号处理
宁静致远20211 天前
STM32模拟IIC读取PCF8563
stm32·单片机·嵌入式硬件
三佛科技-134163842121 天前
宠物洗澡打泡机方案,宠物泡泡机MCU方案开发设计分享
单片机·嵌入式硬件·物联网·智能家居·pcb工艺·宠物
芯岭技术1 天前
低成本315/433M接收芯片 XL420 SOP8封装,支持 1527 等常见 OOK编码
单片机·嵌入式硬件