This time I want to talk about how to use USB to upgrade the microcontroller code. I have done serial port upgrades and network upgrades before. The basic principles of the upgrades are similar, except that the upgrade tools are different. Serial port upgrades use serial ports, network upgrades use TCP/IP, and USB upgrades use USB. Now let's talk about the implementation of USB upgrades.
The modified parts are all in the USB_User group:
Let’s go through each file one by one.
First, let's talk about hw_config.c. This file is similar to the previous project. Due to the needs of demonstration, we initialize a button pin in this file and define a button reading function. This button determines whether the code is upgraded. If the button is pressed at the beginning of the program, it will enter the upgrade mode, otherwise it will jump to the upgrade program code:
/****************************************************** ****************************
* Function Name: DFU_Button_Config.
* Description: Configure the DFU mode selection button.
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_Button_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DFU, ENABLE);
/* Configure DFU button */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = DFU_ENTER_PIN;
GPIO_Init(DFU_ENTER, &GPIO_InitStructure);
}
/****************************************************** ****************************
* Function Name: DFU_Button_Read.
* Description: Read whether the DFU button is pressed
* Input : None.
* Output : None.
* Return: Return 0: the button is pressed; 1: the button is not pressed
*************************************************** *******************************/
uint8_t DFU_Button_Read (void)
{
return GPIO_ReadInputDataBit(DFU_ENTER, DFU_ENTER_PIN);
}
The usb_desc.c file must be modified. The USB function attributes are all defined here. First of all, we must pay attention to the device descriptor. One thing that needs to be emphasized here is that the value of the manufacturer ID field must be 0483, otherwise the computer will not recognize the USB. The product ID can be customized.
/* USB standard device descriptor */
uint8_t DFU_DeviceDescriptor[DFU_SIZ_DEVICE_DESC] =
{
0x12, /*bLength: length, the length of the device descriptor is 18 bytes*/
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType: type, the device descriptor number is 0x01*/
0x00, /*bcdUSB: The USB version used is 2.0*/
0x02,
0x00, /*bDeviceClass: class code used by the device*/
0x00, /*bDeviceSubClass: subclass code used by the device*/
0x00, /*bDeviceProtocol: protocol used by the device*/
0x40, /*bMaxPacketSize: Maximum packet length is 64 bytes*/
0x83, /*idVendor: vendor ID is 0x1234*/
0x04,
0x11, /*idProduct: product ID is 0x1010*/
0xDF,
0x00, /*bcdDevice: The device version number is 2.00*/
0x02,
1, /*iManufacturer: index of manufacturer string*/
2, /*iProduct: index of product string*/
3, /*iSerialNumber: device serial number string index*/
0x01 /*bNumConfiguration: The device has 1 configuration*/
}; /* DFU device descriptor */
Next is the configuration descriptor set, which does not need to be modified. However, the bNumEndpoints field (the number of endpoints used by the interface) of the subsequent interface descriptor needs to be set to 0, because the USB DFU value requires endpoint 0 and does not need other endpoints; the value of the bInterfaceClass (the class used by the interface) field of the interface descriptor is 0xFE, indicating that the DFU class interface is used; the bInterfaceSubClass (the subclass used by the interface) of the interface descriptor is set to 0x01, indicating boot usage; the nInterfaceProtocol of the interface descriptor is set to 0x2, that is, DFU mode; and finally, the index value of the interface string descriptor needs to be set to 4. Next is the DFU function descriptor, where the USB attribute of the bmAttribute field is set to 0x0B (see the code below for the specific meaning); the DetachTimeOut (timeout) is set to 0XFF, indicating that the timeout is 255ms; next, set TransferSize: (transmission length) to 0x400, note that it is represented in two-byte little-endian mode.
/* USB configuration descriptor set (Configuration, Interface, Endpoint, Class, Vendor */
uint8_t DFU_ConfigDescriptor[DFU_SIZ_CONFIG_DESC] =
{
0x09, /*bLength: length, the length of the device string is 9 bytes*/
USB_CONFIGURATION_DESCRIPTOR_TYPE, /*bDescriptorType: type, the type number of the configuration descriptor is 0x2*/
DFU_SIZ_CONFIG_DESC, /*wTotalLength: The total length of the configuration descriptor is 41 bytes*/
0x00,
0x01, /*bNumInterfaces: Configure the number of interfaces supported by 1*/
0x01, /*bConfigurationValue: the value of the configuration*/
0x00, /*iConfiguration: The index value of the string of this configuration. The value 0 indicates that there is no string*/
0xC0, /* bmAttributes: some characteristics of the device, 0xc0 means self-powered, does not support remote wake-up
D7: Reserved must be 1, D6: Whether it is self-powered, D5: Whether it supports remote wake-up, D4~D0: Reserved set to 0*/
0x32, /*The maximum current obtained from the bus is 100mA */
// 0x96, /*MaxPower: How much current the device needs to obtain from the bus, the unit is 2mA, 0x96 means 300mA*/
/****** Descriptor of DFU interface 0 Alternate setting 0************/
0x09, /*bLength: length, the length of the interface descriptor is 9 bytes*/
USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: The type of interface descriptor is 0x4 */
0x00, /*bInterfaceNumber: the number of the interface*/
0x00, /*bAlternateSetting: The alternate number of this interface*/
0x00, /*bNumEndpoints: The number of endpoints used by this interface*/
0xFE, /*bInterfaceClass The class used by this interface is DFU*/
0x01, /*bInterfaceSubClass: The subclass used by this interface 1=BOOT, 0=no boot */
0x02, /*nInterfaceProtocol :DFU mode*/
4, /* iInterface: index of interface string descriptor*/
/******************** DFU function descriptor********************/
0x09, /*blength: The length of the DFU descriptor is 9 bytes*/
0x21, /*The type of the function descriptor is 0x21*/
0x0B, /*bmAttribute
bitCanDnload = 1 (bit 0) Download capability
bitCanUpload = 1 (bit 1) Upload capability
bitManifestationTolerant = 0 (bit 2) Whether the device can communicate via USB after the waiting phase
bitWillDetach = 1 (bit 3) When receiving the DFU_DETACH command, the bus will be detached-attached
Reserved (bit4-6) Reserved
bitAcceleratedST = 0 (bit 7) If this is set to 1, the device will increase the upload speed to 4096 bytes/command*/
0xFF, /*DetachTimeOut: timeout 255 ms*/
0x00,
wTransferSizeB0,
wTransferSizeB1, /* TransferSize: The transfer length is 1024 Byte*/
0x1A, /* bcdDFUVersion: DFU protocol version*/
0x01
};
The following language string descriptor, manufacturer string descriptor, product string descriptor, and serial number string descriptor will not be introduced in detail.
/* Language ID descriptor */
uint8_t DFU_StringLangId[DFU_SIZ_STRING_LANGID] =
{
DFU_SIZ_STRING_LANGID, /*bLength: The length of this descriptor is 4 bytes*/
USB_STRING_DESCRIPTOR_TYPE, /*bDescriptorType: string descriptor type is 0x03*/
0x09, /*bString: Language ID is 0x0409, indicating American English*/
0x04
}; /* LangID = 0x0409: US English*/
/*Manufacturer string descriptor*/
uint8_t DFU_StringVendor[DFU_SIZ_STRING_VENDOR] =
{
DFU_SIZ_STRING_VENDOR, /*bLength: length of the vendor string descriptor*/
USB_STRING_DESCRIPTOR_TYPE, /*bDescriptorType: string descriptor type is 0x03*/
'B' , 0, 'y', 0, ':' , 0, 'z' , 0, 'i', 0, 'y', 0,'e', 0,'3', 0, '3', 0, '4', 0 /*Custom*/
};
/*String descriptor of the product*/
uint8_t DFU_StringProduct[DFU_SIZ_STRING_PRODUCT] =
{
DFU_SIZ_STRING_PRODUCT, /* bLength: string descriptor of the product */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType: string descriptor type is 0x03*/
'z', 0, 'i', 0, 'y', 0, 'e', 0, '3', 0, '3', 0 ,'4',0/*Custom*/
};
/* Product serial number string descriptor */
uint8_t DFU_StringSerial[DFU_SIZ_STRING_SERIAL] =
{
DFU_SIZ_STRING_SERIAL, /* bLength: product serial number*/
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType: string descriptor type is 0x03*/
'1', 0, '2', 0, '3', 0,'4', 0,'5', 0, '6', 0, '7', 0 /*Custom*/
};
The focus is on the interface string descriptor, which defines the upgraded hardware information. The following is our interface descriptor:
/*Interface string descriptor*/
uint8_t DFU_StringInterface0[DFU_SIZ_STRING_INTERFACE0] =
{
DFU_SIZ_STRING_INTERFACE0,
0x03,
// Interface 0: "@Internal Flash /0x08000000/12*001Ka,500*001Kg"
'@', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'n', 0, 'a', 0, 'l ', 0, /* 18 */
' ', 0, 'F', 0, 'l', 0, 'a', 0, 's', 0, 'h', 0, ' ', 0, ' ', 0, /* 16 */
'/', 0, '0', 0, 'x', 0, '0', 0, '8', 0, '0', 0, '0', 0, '0', 0, '0 ', 0, '0', 0, '0', 0, /* 22 */
'/', 0, '1', 0, '2', 0, '*', 0, '0', 0, '0', 0, '1', 0, 'K', 0, 'a ', 0, /* 18 */
',', 0, '5', 0, '0', 0, '0', 0, '*', 0, '0', 0, '0', 0, '1', 0, 'K ', 0, 'g', 0, /* 20 */
};
You can see the comment above: // Interface 0: "@Internal Flash /0x08000000/12*001Ka,500*001Kg", which is the information expressed by this interface descriptor. Next, we will introduce the information of the array content of the interface descriptor in detail:
—— @: Indicates that this is a special mapping descriptor (avoid decoding according to the standard descriptor)
—— /: indicates the separator between different areas
—— Maximum address is 8 bits, starting with "0X"
—— The sector number with two digits at most
—— *: separator between the number of sectors and the sector size
—— Maximum 3-digit sector size (0~999)
—— 1-bit sector size unit: valid input is: B (byte), K (kilo), M (mega)
—— 1-bit sector type:
a(0x41): readable
b(0x42): erasable
c(0x43): readable and writable
d(0x44): writable
e(0x45): readable and writable
f (0x46): writable and erasable
g(0x47): readable, writable and erasable
For example, "@Internal Flash /0x08000000/12*001Ka,500*001Kg" means: the name of the memory is "Internal Flash", the starting address is 0x08000000, the 12*1K space is readable, and the 500*1K space is readable, writable and erasable. By the way, the chip I use is STM32F103ZET6, and the flash space is 512K.
The files usb_istr.c and usb_pwr.c do not need to be modified.
The modification of the usb_prop.c file is relatively large. This file describes the changes between the various states of DFU. The code is still a bit vague and I am not going to explain it in detail. I will just paste the code directly. Some of it has been annotated:
#include "usb_lib.h"
#include "hw_config.h"
#include "usb_conf.h"
#include "usb_prop.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "dfu_mal.h"
uint32_t wBlockNum = 0, wlength = 0;
uint32_t Manifest_State = Manifest_complete;
uint32_t Pointer = ApplicationAddress; //Base address for program erase, read and write
DEVICE Device_Table = //Endpoint information
{
EP_NUM, //Number of endpoints used
1 //Number of endpoints that can be used
};
DEVICE_PROP Device_Property = //Register some DFU related processing functions
{
DFU_init, //initialization
DFU_Reset, //Reset
DFU_Status_In, //status input
DFU_Status_Out, //status output
DFU_Data_Setup, //Establishment with data stage
DFU_NoData_Setup, //Setup without data stage
DFU_Get_Interface_Setting, //Get interface value
DFU_GetDeviceDescriptor, //Get device descriptor
DFU_GetConfigDescriptor, //Get configuration descriptor
DFU_GetStringDescriptor, //Get string descriptor
0, //DFU_EP0Buffer
bMaxPacketSize0 //Maximum packet size*/
};
USER_STANDARD_REQUESTS User_Standard_Requests = //Standard requests
{
DFU_GetConfiguration, //Get configuration value request
DFU_SetConfiguration, //Set configuration value request
DFU_GetInterface, //Get interface value request
DFU_SetInterface, //Set interface value request
DFU_GetStatus, //Get status request
DFU_ClearFeature, // Clear feature request
DFU_SetEndPointFeature, //Set breakpoint feature request
DFU_SetDeviceFeature, //Set device feature request
DFU_SetDeviceAddress //Set device address request
};
ONE_DESCRIPTOR Device_Descriptor = //Register device descriptor information
{
(uint8_t*)DFU_DeviceDescriptor, //Device descriptor address
DFU_SIZ_DEVICE_DESC
};
ONE_DESCRIPTOR Config_Descriptor = //Register configuration descriptor information
{
(uint8_t*)DFU_ConfigDescriptor, //Configuration descriptor address
DFU_SIZ_CONFIG_DESC
};
ONE_DESCRIPTOR DFU_String_Descriptor[5] = //Register string descriptor
{
{ (uint8_t*)DFU_StringLangId, DFU_SIZ_STRING_LANGID }, //language string
{ (uint8_t*)DFU_StringVendor, DFU_SIZ_STRING_VENDOR }, //Vendor string
{ (uint8_t*)DFU_StringProduct, DFU_SIZ_STRING_PRODUCT }, //Product string
{ (uint8_t*)DFU_StringSerial, DFU_SIZ_STRING_SERIAL }, //Serial number string
{ (uint8_t*)DFU_StringInterface0, DFU_SIZ_STRING_INTERFACE0 } //Interface descriptor
};
/* Extern variables ----------------------------------------------- ----------*/
extern uint8_t DeviceState;
extern uint8_t DeviceStatus[6];
/* Private function prototypes --------------------------------------------- --*/
/* Private functions -------------------------------------------------- -----------*/
/****************************************************** ****************************
* Function Name : DFU_init.
* Description : DFU initialization.
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_init(void)
{
DEVICE_INFO *pInfo = &Device_Info;
Get_SerialNum(); //Get the serial number
pInfo->Current_Configuration = 0; //Initialize the current configuration value
PowerOn(); //Connect device
USB_SIL_Init(); //Perform basic device initialization operations
USB_Interrupts_Config(); //Enable USB interrupts
bDeviceState = UNCONNECTED;
}
/****************************************************** ****************************
* Function Name : DFU_Reset.
* Description : DFU reset
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_Reset(void)
{
Device_Info.Current_Configuration = 0;
/* Current Feature initialization */
pInformation->Current_Feature = DFU_ConfigDescriptor[7]; //Get the current feature
_SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
_SetEPType(ENDP0, EP_CONTROL); //Set endpoint 0 as the control endpoint
_SetEPTxStatus(ENDP0, EP_TX_NAK); //Set endpoint 0 to EP_TX_NAK
_SetEPRxAddr(ENDP0, ENDP0_RXADDR); //Set the receiving address of endpoint 0
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); //Set the maximum receive packet length of endpoint 0
_SetEPTxAddr(ENDP0, ENDP0_TXADDR); //Set the send address of endpoint 0
SetEPTxCount(ENDP0, Device_Property.MaxPacketSize); //Set the maximum send packet length of endpoint 0
Clear_Status_Out(ENDP0); //Clear endpoint 0 status
SetEPRxValid(ENDP0); //Set endpoint reception valid
/* Set this device to respond on default address */
SetDeviceAddress(0); //Set the device default address
/* Set the new control state of the device to Attached */
bDeviceState = ATTACHED;
}
/****************************************************** ****************************
* Function Name: DFU_SetConfiguration.
* Description: Update device configuration status
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_SetConfiguration(void)
{
DEVICE_INFO *pInfo = &Device_Info;
if (pInfo->Current_Configuration != 0)
{
/* Device configured */
bDeviceState = CONFIGURED;
}
}
/****************************************************** ****************************
* Function Name: DFU_SetConfiguration.
* Description: Update the addressing status of the device
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_SetDeviceAddress (void)
{
bDeviceState = ADDRESSED;
}
/****************************************************** ****************************
* Function Name: DFU_Status_In.
* Description: DFU status input function
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_Status_In(void)
{}
/****************************************************** ****************************
* Function Name: DFU_Status_Out.
* Description: DFU status output function
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_Status_Out (void)
{
DEVICE_INFO *pInfo = &Device_Info;
uint32_t Addr;
if (pInfo->USBbRequest == DFU_GETSTATUS)
{
if (DeviceState == STATE_dfuDNBUSY) //Current state is in progress
{
if (wBlockNum == 0) //The number of blocks is 0
{
if ((MAL_Buffer[0] == CMD_GETCOMMANDS) && (wlength == 1)) //Get more device performance and which devices DFU supports
{}
else if (( MAL_Buffer[0] == CMD_SETADDRESSPOINTER ) && (wlength == 5)) //Set address pointer
{
Pointer = MAL_Buffer[1];
Pointer += MAL_Buffer[2] << 8;
Pointer += MAL_Buffer[3] << 16;
Pointer += MAL_Buffer[4] << 24;
}
else if (( MAL_Buffer[0] == CMD_ERASE ) && (wlength == 5)) //Send erase address sector command
{
Pointer = MAL_Buffer[1]; //Get the download address
Pointer += MAL_Buffer[2] << 8;
Pointer += MAL_Buffer[3] << 16;
Pointer += MAL_Buffer[4] << 24;
MAL_Erase(Pointer); //Erase the address
}
}
else if (wBlockNum > 1) //The number of blocks is greater than 1
{
Addr = ((wBlockNum - 2) * wTransferSize) + Pointer; //Get the address of the data written into the memory
MAL_Write(Addr, wlength); //Write data
}
wlength = 0;
wBlockNum = 0;
DeviceState = STATE_dfuDNLOAD_SYNC; //Set the state to download synchronization
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
return;
}
else if (DeviceState == STATE_dfuMANIFEST) //Display process
{
DFU_write_crc(); //Write CRC check
return;
}
}
return;
}
/****************************************************** ****************************
* Function Name: DFU_Data_Setup.
* Description: Process special class requests with data
* Input : RequestNb.
* Output : None.
* Return: USB_SUCCESS or USB_UNSUPPORT.
*************************************************** *******************************/
RESULT DFU_Data_Setup(uint8_t RequestNo)
{
uint8_t *(*CopyRoutine)(uint16_t);
CopyRoutine = NULL;
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) //Class request, the recipient of the request is the interface
{
if (RequestNo == DFU_UPLOAD && (DeviceState == STATE_dfuIDLE
|| DeviceState == STATE_dfuUPLOAD_IDLE ))//Device is idle, upload is idle
{
CopyRoutine = UPLOAD; //The function pointer points to the UPLOAD function
}
else if (RequestNo == DFU_DNLOAD && (DeviceState == STATE_dfuIDLE
|| DeviceState == STATE_dfuDNLOAD_IDLE))//Device is idle, download is idle
{
DeviceState = STATE_dfuDNLOAD_SYNC; //Set the device state to download synchronization
CopyRoutine = DNLOAD; //Function pointer points to DNDOWN function
}
else if (RequestNo == DFU_GETSTATE) //DFU_GETSTATE request
{
CopyRoutine = GETSTATE; //Function pointer points to GETSTATE function
}
else if (RequestNo == DFU_GETSTATUS) //DFU_GETSTATUS request
{
CopyRoutine = GETSTATUS; //Function pointer points to GETSTATUS function
}
else
{
return USB_UNSUPPORT;
}
}
else
{
return USB_UNSUPPORT;
}
if (CopyRoutine == NULL)
{
return USB_UNSUPPORT;
}
pInformation->Ctrl_Info.CopyData = CopyRoutine; //Register this function pointer
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0); //Point to the function pointed to by the overview pointer
return USB_SUCCESS;
}
/****************************************************** ****************************
* Function Name: DFU_NoData_Setup.
* Description: Handle the No data class specific requests.
* Input : Request Nb.
* Output : None.
* Return: USB_SUCCESS or USB_UNSUPPORT.
*************************************************** *******************************/
RESULT DFU_NoData_Setup(uint8_t RequestNo)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) //Class request
{
/*DFU_NDLOAD*/
if (RequestNo == DFU_DNLOAD) //DFU_DNLOAD request
{
/* End of DNLOAD operation*/
if (DeviceState == STATE_dfuDNLOAD_IDLE || DeviceState == STATE_dfuIDLE ) //Download idle or device idle
{
Manifest_State = Manifest_In_Progress; //Set the display state to display progress
DeviceState = STATE_dfuMANIFEST_SYNC; //Set display synchronization
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
DeviceStatus[4] = DeviceState;
return USB_SUCCESS;
}
}
/*DFU_UPLOAD*/
else if (RequestNo == DFU_UPLOAD) //DFU_UPLOAD command
{
DeviceState = STATE_dfuIDLE; //Set device to idle
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
DeviceStatus[4] = DeviceState;
return USB_SUCCESS;
}
/*DFU_CLRSTATUS*/
else if (RequestNo == DFU_CLRSTATUS) //DFU_CLRSTATUS command
{
if (DeviceState == STATE_dfuERROR) //dfu error
{
DeviceState = STATE_dfuIDLE; // Clear error status
DeviceStatus[0] = STATUS_OK;/*bStatus*/
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0; /*bwPollTimeout=0ms*/
DeviceStatus[4] = DeviceState;/*bState*/
DeviceStatus[5] = 0;/*iString*/
}
else
{ /*State Error*/
DeviceState = STATE_dfuERROR; //Set the device status to upgrade error
DeviceStatus[0] = STATUS_ERRUNKNOWN;/*bStatus*/
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0; /*bwPollTimeout=0ms*/
DeviceStatus[4] = DeviceState;/*bState*/
DeviceStatus[5] = 0;/*iString*/
}
return USB_SUCCESS;
}
/*DFU_ABORT*/
else if (RequestNo == DFU_ABORT) //DFU_ABORT terminates the command
{
if (DeviceState == STATE_dfuIDLE || DeviceState == STATE_dfuDNLOAD_SYNC
|| DeviceState == STATE_dfuDNLOAD_IDLE || DeviceState == STATE_dfuMANIFEST_SYNC
|| DeviceState == STATE_dfuUPLOAD_IDLE ) //Device idle or upload synchronization or download idle or display synchronization or upload idle
{
DeviceState = STATE_dfuIDLE; //Set device to idle
DeviceStatus[0] = STATUS_OK;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0; /*bwPollTimeout=0ms*/
DeviceStatus[4] = DeviceState;
DeviceStatus[5] = 0; /*iString*/
wBlockNum = 0;
wlength = 0;
}
return USB_SUCCESS;
}
}
return USB_UNSUPPORT;
} /* End of DFU_NoData_Setup */
/****************************************************** ****************************
* Function Name: DFU_GetDeviceDescriptor.
* Description: Get device descriptor
* Input : Length.
* Output : None.
* Return: Return the address of the device descriptor
*************************************************** *******************************/
uint8_t *DFU_GetDeviceDescriptor(uint16_t Length)
{
return Standard_GetDescriptorData(Length, &Device_Descriptor);
}
/****************************************************** ****************************
* Function Name: DFU_GetConfigDescriptor.
* Description: Get configuration descriptor
* Input : Length.
* Output : None.
* Return: Returns the address of the configuration descriptor
*************************************************** *******************************/
uint8_t *DFU_GetConfigDescriptor(uint16_t Length)
{
return Standard_GetDescriptorData (Length, &Config_Descriptor);
}
/****************************************************** ****************************
* Function Name: DFU_GetStringDescriptor.
* Description: Get string description according to index
* Input : Length.
* Output : None.
* Return: Return the address of the string descriptor
*************************************************** *******************************/
uint8_t *DFU_GetStringDescriptor(uint16_t Length)
{
uint8_t wValue0 = pInformation->USBwValue0;
if (wValue0 > 8)
{
return NULL;
}
else
{
return Standard_GetDescriptorData(Length, &DFU_String_Descriptor[wValue0]);
}
}
/****************************************************** ****************************
* Function Name: DFU_Get_Interface_Setting.
* Description: Tests whether the interface and backup interface are supported
* Input : - Interface : Interface number
* - AlternateSetting: Alternate interface number
* Output : None.
* Return: USB_SUCCESS or USB_UNSUPPORT.
*************************************************** *******************************/
RESULT DFU_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting)
{
if (AlternateSetting > 3)
{
return USB_UNSUPPORT; /* In this application we don't have more than 3 AlternateSettings */
}
else if (Interface > 2)
{
return USB_UNSUPPORT; /* In this application we have only 1 interfaces */
}
return USB_SUCCESS;
}
/****************************************************** ****************************
* Function Name : UPLOAD
* Description : Upload
* Input : Length.
* Output : None.
* Return: Pointer to data.
*************************************************** *******************************/
uint8_t *UPLOAD(uint16_t Length)
{
DEVICE_INFO *pInfo = &Device_Info; //Get device information
uint8_t B1, B0; //16bit high 8 bits and low 8 bits
uint16_t offset, returned;
uint8_t *Phy_Addr = NULL;
uint32_t Addr = 0;
B0 = pInfo->USBwValues.bw.bb0; //Get the lower 8 bits of the requested wValue
B1 = pInfo->USBwValues.bw.bb1; //Get the high 8 bits of the requested wValue
wBlockNum = (uint16_t)B1;
wBlockNum = wBlockNum * 0x100;
wBlockNum += (uint16_t)B0; //Update wBlockNum
B0 = pInfo->USBwLengths.bw.bb0; //Get the lower 8 bits of the requested wLength
B1 = pInfo->USBwLengths.bw.bb1; //Get the high 8 bits of the requested wLength
wlength = (uint16_t)B0;
wlength = wlength * 0x100;
wlength += (uint16_t)B1; //Update wLength
offset = pInformation->Ctrl_Info.Usb_wOffset; //Get data offset
if (wBlockNum == 0) //wBlockNum is equal to 0, indicating no data transmission
{
if (wlength > 3)
{
DeviceState = STATE_dfuIDLE;
}
else
{
DeviceState = STATE_dfuUPLOAD_IDLE;
}
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
MAL_Buffer[0] = CMD_GETCOMMANDS; //Get commands
MAL_Buffer[1] = CMD_SETADDRESSPOINTER; //Set address pointer
MAL_Buffer[2] = CMD_ERASE; // Erase the sector containing the address
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = 3;
return NULL;
}
return(&MAL_Buffer[0]);
}
else if (wBlockNum > 1) //wBlockNum is greater than 1
{
DeviceState = STATE_dfuUPLOAD_IDLE;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
Addr = ((wBlockNum - 2) * wTransferSize) + Pointer; //Calculate the address of the data to be uploaded in the memory
Phy_Addr = MAL_Read(Addr, wlength); //Read data
returned = wlength - offset; //Get the starting address of the uploaded data
if (Length == 0) //The length to upload is 0
{
pInformation->Ctrl_Info.Usb_wLength = returned;
return NULL;
}
return(Phy_Addr + offset);
}
else //That is, wBlockNum=1, unsupported wBlockNum
{
DeviceState = STATUS_ERRSTALLEDPKT;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
return NULL;
}
}
/****************************************************** ****************************
* Function Name : DNLOAD
* Description : Download
* Input : Length.
* Output : None.
* Return: Returns a pointer to the data
*************************************************** *******************************/
uint8_t *DNLOAD (uint16_t Length)
{
DEVICE_INFO *pInfo = &Device_Info; //Get device information
uint8_t B1, B0; //16bit high 8 bits and low 8 bits
uint16_t offset, returned;
B0 = pInfo->USBwValues.bw.bb0; //Get the lower 8 bits of the requested wValue
B1 = pInfo->USBwValues.bw.bb1; //Get the high 8 bits of the requested wValue
wBlockNum = (uint16_t)B1;
wBlockNum = wBlockNum * 0x100;
wBlockNum += (uint16_t)B0; //Update wBlockNum
B0 = pInfo->USBwLengths.bw.bb0; //Get the lower 8 bits of the requested wLength
B1 = pInfo->USBwLengths.bw.bb1; //Get the high 8 bits of the requested wLength
wlength = (uint16_t)B0;
wlength = wlength * 0x100;
wlength += (uint16_t)B1; //Update wLength
offset = pInfo->Ctrl_Info.Usb_wOffset;
DeviceState = STATE_dfuDNLOAD_SYNC;
DeviceStatus[4] = DeviceState;
returned = wlength - offset; //point to the starting address of the data
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = returned;
return NULL;
}
return((uint8_t*)MAL_Buffer + offset);
}
/****************************************************** ****************************
* Function Name : GETSTATE.
* Description : Get STATE request function.
* Input : Length.
* Output : None.
* Return: Pointer to data.
*************************************************** *******************************/
uint8_t *GETSTATE(uint16_t Length)
{
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = 1;
return NULL;
}
else
return(&DeviceState);
}
/****************************************************** ****************************
* Function Name : GETSTATUS.
* Description: Get Status request function
* Input : Length.
* Output : None.
* Return: Pointer to data.
*************************************************** *******************************/
uint8_t *GETSTATUS(uint16_t Length)
{
switch (DeviceState)
{
case STATE_dfuDNLOAD_SYNC:
if (wlength != 0)
{
DeviceState = STATE_dfuDNBUSY;
DeviceStatus[4] = DeviceState;
if ((wBlockNum == 0) && (MAL_Buffer[0] == CMD_ERASE))
{
MAL_GetStatus(Pointer, 0, DeviceStatus);
}
else
{
MAL_GetStatus(Pointer, 1, DeviceStatus);
}
}
else /* (wlength==0)*/
{
DeviceState = STATE_dfuDNLOAD_IDLE;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
}
break;
case STATE_dfuMANIFEST_SYNC :
if (Manifest_State == Manifest_In_Progress)
{
DeviceState = STATE_dfuMANIFEST;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 1; /*bwPollTimeout = 1ms*/
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
//break;
}
else if (Manifest_State == Manifest_complete && Config_Descriptor.Descriptor[20]
& 0x04)
{
DeviceState = STATE_dfuIDLE;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
//break;
}
break;
default :
break;
}
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = 6;
return NULL;
}
else
return(&(DeviceStatus[0]));
}
/****************************************************** ****************************
* Function Name: DFU_write_crc.
* Description: DFU write CRC
* Input : None.
* Output : None.
* Return : None.
*************************************************** *******************************/
void DFU_write_crc(void)
{
Manifest_State = Manifest_complete;
if (Config_Descriptor.Descriptor[20] & 0x04)
{
DeviceState = STATE_dfuMANIFEST_SYNC;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
return;
}
else
{
DeviceState = STATE_dfuMANIFEST_WAIT_RESET;
DeviceStatus[4] = DeviceState;
DeviceStatus[1] = 0;
DeviceStatus[2] = 0;
DeviceStatus[3] = 0;
Reset_Device();
return;
}
}
Next, we will talk about the file flash_if.c. The premise for implementing this file is that the stm32f10x_flash.c library file has been added to the project. This file has only four files: FLASH_If_Init(), FLASH_If_Erase(), FLASH_If_Write(), and FLASH_If_Read(). First, let's talk about FLASH_If_Init(). Because flash is where the program is stored and can be used as soon as it is powered on, there is no need to initialize it. This function can be an empty function:
uint16_t FLASH_If_Init(void)
{
return MAL_OK;
}
Then comes FLASH_If_Erase(uint32_t SectorAddress), this function calls the flash erase page function FLASH_ErasePage():
uint16_t FLASH_If_Erase(uint32_t SectorAddress)
{
FLASH_ErasePage(SectorAddress);
return MAL_OK;
}
As for the FLASH_If_Write() function, it is a little more complicated. This function needs to determine whether the length to be written is sub-aligned. If it is not word-aligned, it needs to be filled to be word-aligned and then written to the flash. It should be noted here that our STM32 is 32-bit, and word alignment is required for both writing and reading.
uint16_t FLASH_If_Write(uint32_t SectorAddress, uint32_t DataLength)
{
uint32_t idx = 0;
if (DataLength & 0x3) /* Not an aligned data */
{
for (idx = DataLength; idx < ((DataLength & 0xFFFC) + 4); idx++)
{
MAL_Buffer[idx] = 0xFF;
}
}
/* Data received are Word multiple */
for (idx = 0; idx < DataLength; idx = idx + 4)
{
FLASH_ProgramWord(SectorAddress, *(uint32_t *)(MAL_Buffer + idx));
SectorAddress += 4;
}
return MAL_OK;
}
Then comes the read function uint8_t *FLASH_If_Read (uint32_t SectorAddress, uint32_t DataLength). Here we directly return the address to be read. This function will only be called during verification.
uint8_t *FLASH_If_Read (uint32_t SectorAddress, uint32_t DataLength)
{
return (uint8_t*)(SectorAddress);
}
Next is dfu_mal.c. This file includes MAL_Init(), MAL_Erase(), MAL_Write(), MAL_Read(), MAL_GetStatus():
uint16_t MAL_Init(void)
{
FLASH_If_Init(); //Internal Flash initialization
return MAL_OK;
}
/****************************************************** ****************************
* Function Name : MAL_Erase
* Description : Erase sector
* Input : None
* Output : None
* Return : None
*************************************************** *******************************/
uint16_t MAL_Erase(uint32_t SectorAddress)
{
switch (SectorAddress & MAL_MASK) //See address
{
case INTERNAL_FLASH_BASE: //If it is in the INTERNAL FLASH address pool
pMAL_Erase = FLASH_If_Erase; //Erase function pointer points to FLASH_If_Erase
break;
default:
return MAL_FAIL;
}
return pMAL_Erase(SectorAddress); //Point to erase function
}
/****************************************************** ****************************
* Function Name : MAL_Write
* Description : Write sector
* Input : None
* Output : None
* Return : None
*************************************************** *******************************/
uint16_t MAL_Write (uint32_t SectorAddress, uint32_t DataLength)
{
switch (SectorAddress & MAL_MASK) //View address
{
case INTERNAL_FLASH_BASE: //If it is in the INTERNAL FLASH address pool
pMAL_Write = FLASH_If_Write; //Write function pointer points to FLASH_If_Write
break;
default:
return MAL_FAIL;
}
return pMAL_Write(SectorAddress, DataLength); //Call the write sector function
}
/****************************************************** ****************************
* Function Name : MAL_Read
* Description : Degree sector
* Input : None
* Output : None
* Return : Buffer pointer
*************************************************** *******************************/
uint8_t *MAL_Read (uint32_t SectorAddress, uint32_t DataLength)
{
switch (SectorAddress & MAL_MASK) //View address
{
case INTERNAL_FLASH_BASE: //If it is in the INTERNAL FLASH address pool
pMAL_Read = FLASH_If_Read; //Read function pointer points to FLASH_If_Read
break;
default:
return 0;
}
return pMAL_Read (SectorAddress, DataLength); //Call sector function
}
/****************************************************** ****************************
* Function Name: MAL_GetStatus
* Description: Get status
* Input : None
* Output : None
* Return : MAL_OK
*************************************************** *******************************/
uint16_t MAL_GetStatus(uint32_t SectorAddress, uint8_t Cmd, uint8_t *buffer)
{ //Corresponding options for address lookup timing table
uint8_t x = (SectorAddress >> 26) & 0x03 ; /* 0x000000000 --> 0 */
/* 0x640000000 --> 1 */
/* 0x080000000 --> 2 */
uint8_t y = Cmd & 0x01;
SET_POLLING_TIMING(TimingTable[x][y]); /* x: erase/write timing*/
/* y: Media */
return MAL_OK;
}
Finally, let’s talk about the main function:
typedef void (*pFunction)(void);
uint8_t DeviceState;
uint8_t DeviceStatus[6];
pFunction Jump_To_Application;
uint32_t JumpAddress;
int main(void)
{
BSP_Init();
printf(" |============================================== =|\r\n");
printf(" STM32 DFU program starts \r\n");
printf("|============================================== =|\r\n");
if (DFU_Button_Read() != 0x00)
{
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) // Check if the jump address area is correct
{
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); //Get the RESET interrupt vector address of the jump program
Jump_To_Application = (pFunction) JumpAddress; //Force conversion into function pointer
__set_MSP(*(__IO uint32_t*) ApplicationAddress); //Write the address to R14
Jump_To_Application(); //Call the function. When the function call ends, PC points to the address of R14.
}
}
FLASH_Unlock();
/* Enter DFU mode */
DeviceState = STATE_dfuERROR; //The program points to this sentence, indicating that the DFU jump is unsuccessful
DeviceStatus[0] = STATUS_ERRFIRMWARE;
DeviceStatus[4] = DeviceState;
USB_Configuration(); //Initialize USB
while(1)
{ //LED1 flashes
LED1_Toggle();
Delay_ms(1000);
}
}
There are three places that need special explanation:
1. Implementation of program jump. We define the upgrade address in hw_config.h: #define ApplicationAddress 0x08005000. In the main function, the first press will check whether the upgrade address is valid if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000). This sentence actually checks whether the upgrade address is within the address range of BANK1, because the flash is in BANK1. You can see that a function pointer type function structure is defined above: typedef
void (*pFunction)(void); A jump address variable uint32_t JumpAddress is also defined; You can see JumpAddress =
*(__IO uint32_t*) (ApplicationAddress + 4); In this sentence, JumpAddress is assigned the address of the upgrade address + 4. It can be known that this address points to the main stack of the upgrade code. As for why someone asked: why not use ApplicationAddress directly? The reason is very simple, because ApplicationAddresss is a constant and cannot be used directly, so the JumpAddress variable is defined here. Then a function pFunction is defined using this function structure
Jump_To_Application;, the function pointer Jump_To_Application in the code Jump_To_Application = (pFunction) JumpAddress; points to the JumpAddress address. Then the code calls __set_MSP(*(__IO
uint32_t*) ApplicationAddress); This sentence means writing the address of ApplicationAddress into R14 register. Finally, call Jump_To_Application() function. When the function call returns, the PC pointer will return to the address saved in R14, and then the program will jump to this address to execute. This is the implementation of program jump.
2. The flash must be unlocked, otherwise the upgrade program cannot be burned into the flash, so the FLASH_Unlock() function must be called.
3. The device state must be given. Initially, DeviceState must be set to STATE_dfuERROR and DeviceStatus[0] must be set to STATUS_ERRFIRMWARE.
Previous article:STM32 USB DFU firmware upgrade must read
Next article:STM32 SPI Flash DFU
Recommended ReadingLatest update time:2024-11-16 23:40
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- EEWORLD University ---- QEMU network programming video
- CircuitPython 8.0.0 Alpha 1 released
- Understanding Metastability
- Find component model
- Programming mode, realize activity status recognition and environmental data collection, and output through USB virtual serial port
- I3G4250D 3-axis digital output gyroscope data sheet, package, driver code
- We are just two materials away from making it 100% domestically made. Are there no domestic alternatives for USB connectors and MAX6369KA?
- Live streaming entrance is now open | Renesas Electronics R-Car Advanced Driver Assistance System Solution
- Things about MSP430 MCU (serial, updated to the 10th article on March 4, 2019)
- 【GD32E231_DIY】-04: 6-channel PWM output