DFU 流程

Software update handling 通过 READ_DFUWRITE_DFU functions 完成。

必须使用以下 interrupt lines 推导 state:

  • DFU_START_IRQ
  • DFU_PACKET_IRQ
  • DFU_STOP_IRQ

每当 software update triggered 时,DFU_START_IRQ 会变为 high。

然后 fixture 必须准备接收 firmware,并返回以下 response 之一:

  • 0x0: DFU_ACCEPT
  • 0x1 - 0xFF: DFU_ABORT

Fixture 使用 WRITE_DFU function response。Response timeout 为 20 seconds。写入 response 后会 disable DFU_START_IRQ flag。

接受 DFU 后,Vision Controller 开始发送 firmware packets。每个 packet 都有唯一 id,并向上计数。长度可变,最大 200 Byte。Default length 为 128 Byte。packetNr 使用 Big Endian,不是 Little Endian。

data[0]data[1]data[2]data[3]...data[202]
packetNrpacketNrdata[0]dmx[1]...dmx[200]

读取 packet 后,下一个 packet 会自动传输,不需要额外 response。

如果发生 error,可以通过写入大于 0 的 response(0x1 - 0xFF)提前 abort DFU。

如果 30 seconds 内没有读取 packet,DFU 会 abort。第一个 packet 到达前最多可能需要 30 seconds请确保不要在该 timeout 之前 abort。

收到最后一个 packet 后,DFU_STOP_IRQ flag 会变为 high,用于表示所有 packets 已传输完成,DFU transmission 已结束。

此时 fixture 必须发送最后一个 response。Response 应为以下之一:

  • 0x0: DFU_SUCCESS
  • 0x1: DFU_ERROR_FLASH_ERASE
  • 0x2: DFU_ERROR_NOT_IN_PROGRESS
  • 0x3: DFU_ERROR_CORRUPTED_HEADER
  • 0x4: DFU_ERROR_CORRUPTED_IMAGE
  • 0x5: DFU_ERROR_INVALID_IMAGE_VERSION
  • 0x6: DFU_ERROR_NO_PREAMBLE
  • 0x7: DFU_ERROR_INVALID_FIXTURE_ID
  • 0x8: DFU_ERROR_UNKNOWN
  • 0x9: DFU_ERROR_SIGNATURE_NECESSARY_NOT_FOUND
  • 0x0A: DFU_ERROR_PACKET_TIMEOUT
  • 0x0B - 0xFF: Reserved
DFU process

下面是一个 pseudo code example,用于在 application firmware 中接收 data,而不是像 example 那样使用 boot-loader 接收 data:

#define DFU_BOOT_DELAY_MS     1000
#define MAX_FIRMWARE_SIZE     (256 * 1024u) // example

typedef enum
{
    DFU_STATE_IDLE = 0,
    DFU_STATE_RECEIVING,
    DFU_STATE_ERROR,
    DFU_STATE_BOOT_PENDING
} dfu_state_t;

static dfu_state_t dfuState = DFU_STATE_IDLE;
static uint32_t dfuDataCounter = 0;
static uint16_t dfuPacketCounter = 0;

static uint8_t firmwareData[MAX_FIRMWARE_SIZE]; // Just dummy, use flash to write here
 
static void Dfu_Reset(void)
{
    dfuState = DFU_STATE_IDLE;
    dfuDataCounter = 0;
    dfuPacketCounter = 0;
}

static void Dfu_Abort(uint8_t errorCode)
{
    vision_writeDfu(errorCode);
    Dfu_Reset();
}

static bool Dfu_StoragePrepare(void)
{
    // Erase temporary DFU flash area here.
    // Return false if erase failed.
    return true;
}

static bool Dfu_StorageWrite(uint32_t offset, const uint8_t *data, uint16_t length)
{
    // Write data to temporary DFU flash area.
    // Handle flash alignment here.
    // Return false if flash write failed.
    (void)offset;
    (void)data;
    (void)length;

    return true;
}

static bool Dfu_CheckFirmwareDataValid(uint32_t imageSize)
{
    if (imageSize == 0)
    {
        return false;
    }

    if (imageSize > MAX_FIRMWARE_SIZE)
    {
        return false;
    }

    // Recommended checks:
    // - image header valid
    // - target device / hardware ID valid
    // - firmware size valid
    // - CRC/hash valid
    // - version valid
    //
    // Final signature/security validation should also be done by bootloader.

    return true;
}

static void Dfu_StartBootloaderTimer(void)
{
    // Start one-shot timer, for example 100 ms.
    // After timeout call Dfu_BootloaderTimerElapsed().
}

static void Dfu_SetBootloaderPendingFlag(void)
{
    // Store persistent bootloader request.
    // Options:
    // - backup register
    // - retained RAM magic value
    // - flash flag
    //
    // Bootloader must check this after reset.
}


static void Dfu_BootloaderTimerElapsed(void)
{
    if (dfuState != DFU_STATE_BOOT_PENDING)
    {
        return;
    }

    Dfu_SetBootloaderPendingFlag();

    __disable_irq();

    // Optional:
    // deinit peripherals
    // flush logs
    // stop radio/SPI/UART cleanly

    NVIC_SystemReset();
}


  // Callback Implementation SPI Library ---------------------------------------------------------------------
 
 static void DfuFlagReceivedCallback(vision_dfu_flag_t flag)
{

	if(flag == VISION_DFUFLAG_STOP)
	{
		if (dfuState != DFU_STATE_RECEIVING)
		{
			Dfu_Abort(DFU_ERROR_NOT_IN_PROGRESS);
			return;
		}

		if (!Dfu_CheckFirmwareDataValid(dfuDataCounter))
		{
			Dfu_Abort(DFU_ERROR_CORRUPTED_IMAGE);
			return;
		}

		dfuState = DFU_STATE_BOOT_PENDING;

		// Tell sender that application-side receive/validation was okay.
		vision_writeDfu(DFU_SUCCESS);

		// Do not reset state here.
		// Wait until the response had enough time to leave the device.
		Dfu_StartBootloaderTimer();
		return;
	} 
	else if(flag == VISION_DFUFLAG_START)
	{
		Dfu_Reset();

		if (!Dfu_StoragePrepare())
		{
			Dfu_Abort(DFU_ERROR_FLASH);
			return;
		}

		dfuState = DFU_STATE_RECEIVING;

		// State is valid before response is sent.
		vision_writeDfu(DFU_SUCCESS);
		return;
	}
}
static void DfuDataReceivedCallback(uint16_t packetNr,const uint8_t* buf, uint16_t length)
{
	 if (dfuState != DFU_STATE_RECEIVING)
    {
        vision_writeDfu(DFU_ERROR_NOT_IN_PROGRESS);
        return;
    }

    if (buf == NULL || length == 0)
    {
        Dfu_Abort(DFU_ERROR_INVALID_PACKET);
        return;
    }

    if (packetNr != dfuPacketCounter)
    {
        Dfu_Abort(DFU_ERROR_WRONG_PACKETNR);
        return;
    }

    if ((uint32_t)length > (MAX_FIRMWARE_SIZE - dfuDataCounter))
    {
        Dfu_Abort(DFU_ERROR_IMAGE_TOO_LARGE);
        return;
    }

    if (!Dfu_StorageWrite(dfuDataCounter, buf, length))
    {
        Dfu_Abort(DFU_ERROR_FLASH);
        return;
    }

    dfuDataCounter += length;
    dfuPacketCounter++;

}
© KKT Künzler Technologies GbR.
Developed by KKT - Künzler Technologies GbR