안녕하세요, 얼른 올려야 했는데 너무 늦었네요.
지난 번에 CUBEMX 툴로 부트로더 만들어서 실행하면 윈도우 장치 드라이버에 DFU 드라이버가 생성되는 것 까지 했었죠.
여기까지 1차로 진행이 잘 된 것이고,
두번째로 추가할 코드들이 있습니다.
두개의 파일에 손을 대야 합니다.
main.c 과 usbd_dfu_if.c 입니다.
먼저 main.c 에서 고쳐야 할 내용은 다음과 같습니다.
1. APP 프로그램(부트로더 프로그램이 아닌) 이 현재 USBD_DFU_APP_DEFAULT_ADD 위치에 들어 있는지 검사하는 내용.
2. APP 가 존재하면 APP 프로그램으로 점프.
3. 강제로 부트로더 진입조건을 설정하여 DFU Upgrade 모드로 들어갈 수 있다.(User Switch를 누름으로써)
3. 존재하지 않으면 DFU Upgrade 프로그램 실행.
4. MX_USB_DEVICE_Init() 함수는 Cube MX 툴이 Source 코드를 생성할 때마다 자동으로 다시 위치를 앞쪽에 잡아 놓는데, APP 프로그램이 메모리에 없을때만 동작하도록 뒷쪽으로 위치 이동 시켜야 합니다.
해당 코드는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 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(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); //MX_USB_DEVICE_Init(); /* USER CODE BEGIN 2 */ HAL_Delay(500); printf("DFU Program Start.. \n\r"); // Test if User button on the Necleo kit is pressed if (HAL_GPIO_ReadPin(USER_SW_IN_GPIO_Port,USER_SW_IN_Pin) != GPIO_PIN_RESET) { printf("USBD_DFU_APP_DEFAULT_ADD ..[%08X]\n\r",(*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD)); // Check Vector Table: Test if user code is programmed starting from address // "APPLICATION_ADDRESS" if (((*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000 ) == 0x20000000) { printf("APP Start.. \n\r"); // Jump to user application JumpAddress = *(__IO uint32_t*) (USBD_DFU_APP_DEFAULT_ADD + 4); Jump_To_Application = (pFunction) JumpAddress; // Initialize user application's Stack Pointer __set_MSP(*(__IO uint32_t*) USBD_DFU_APP_DEFAULT_ADD); Jump_To_Application(); } } printf("DFU Upgrade Mode Start.. \n\r"); MX_USB_DEVICE_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } | cs |
여기서 설명이 좀 필요한 부분이 APP 시작 주소를 검사하여 프로그램이 메모리에 들어있는지를 검사하는 코드인데,
해당 코드는 다음과 같습니다.
1 2 3 4 | if (((*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000 ) == 0x20000000) { } | cs |
APP 시작 주소(USBD_DFU_APP_DEFAULT_ADD)는 나중에 부트로더에 의해 Upload될 APP프로그램이 위치할 시작 주소입니다.
이 위치에는 Stack 포인터가 위치하는데, 이 Stack Pointer 는 램 영역 내에 주소값을 갖어야 정상적인 프로그램으로 인식합니다.
128KByte 의 램영역은 0x2000 0000 ~ 0x2001 FFFF 입니다.
위의 계산식을 풀어서 쓰면
1 2 3 4 | if ( (*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) >= 0x20000000 && \ (*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) < 0x20020000 ) { } | cs |
와 같습니다.
풀어서 보면 쉽게 아시겠죠? 저도 처음에 왜 이렇게 쓰는지 골머리를 알았습니다. ^^
그다음에 __set_MSP() 함수로 스택포인터를 초기화한 후, user Application 프로그램으로 점프하면 Upload된 user 프로그램이 실행됩니다.
이번에는 usbd_dfu_if.c 의 내용을 수정해야 하는데, 추가할 내용이 꽤 많네요.
내용의 설명은 하지 않고 수정할 코드만 표시하겠습니다. 내용은 내부 Flash 메모리 읽기/쓰기/지우기 내용들입니다.
내부 Flash 메모리 읽기/쓰기/지우기 내용은 나중에 따로 다룰 예정입니다.
다음과 같은 함수를 추가합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | /* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */ static uint32_t GetSectorSize(uint32_t Sector); static uint32_t GetSector(uint32_t Address); /* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */ /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */ static uint32_t GetSector(uint32_t Address) { uint32_t sector = 0; if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)) { sector = FLASH_SECTOR_0; } else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)) { sector = FLASH_SECTOR_1; } else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)) { sector = FLASH_SECTOR_2; } else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)) { sector = FLASH_SECTOR_3; } else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)) { sector = FLASH_SECTOR_4; } else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)) { sector = FLASH_SECTOR_5; } else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)) { sector = FLASH_SECTOR_6; } else/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_7))*/ { sector = FLASH_SECTOR_7; } return sector; } /** * @brief Gets sector Size * @param None * @retval The size of a given sector */ static uint32_t GetSectorSize(uint32_t Sector) { uint32_t sectorsize = 0x00; if((Sector == FLASH_SECTOR_0) || (Sector == FLASH_SECTOR_1) || (Sector == FLASH_SECTOR_2) ||\ (Sector == FLASH_SECTOR_3) ) { sectorsize = 16 * 1024; } else if(Sector == FLASH_SECTOR_4) { sectorsize = 64 * 1024; } else { sectorsize = 128 * 1024; } return sectorsize; } | cs |
내용이 빈 함수들에 다음과 같이 추가합니다.
uint16_t MEM_If_Init_FS(void) 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | uint16_t MEM_If_Init_FS(void) { /* USER CODE BEGIN 0 */ return (USBD_OK); /* USER CODE END 0 */ } // 다음과 같이 수정 uint16_t MEM_If_Init_FS(void) { /* USER CODE BEGIN 0 */ HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); return (USBD_OK); /* USER CODE END 0 */ } | cs |
uint16_t MEM_If_DeInit_FS(void) 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | uint16_t MEM_If_DeInit_FS(void) { /* USER CODE BEGIN 1 */ return (USBD_OK); /* USER CODE END 1 */ } // 다음과 같이 수정 uint16_t MEM_If_DeInit_FS(void) { /* USER CODE BEGIN 1 */ HAL_FLASH_Lock(); return (USBD_OK); /* USER CODE END 1 */ } | cs |
uint16_t MEM_If_Erase_FS(uint32_t Add) 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | uint16_t MEM_If_Erase_FS(uint32_t Add) { /* USER CODE BEGIN 2 */ return (USBD_OK); /* USER CODE END 2 */ } // 다음과 같이 수정 uint16_t MEM_If_Erase_FS(uint32_t Add) { /* USER CODE BEGIN 2 */ uint32_t UserStartSector; uint32_t SectorError; FLASH_EraseInitTypeDef pEraseInit; MEM_If_Init_FS(); UserStartSector = GetSector(Add); pEraseInit.TypeErase = TYPEERASE_SECTORS; pEraseInit.Sector = UserStartSector; pEraseInit.NbSectors = 3; pEraseInit.VoltageRange = VOLTAGE_RANGE_3; if(HAL_FLASHEx_Erase(&pEraseInit,&SectorError)!=HAL_OK) { return (USBD_FAIL); } return (USBD_OK); /* USER CODE END 2 */ } | cs |
uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len) 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len) { /* USER CODE BEGIN 3 */ return (USBD_OK); /* USER CODE END 3 */ } // 다음과 같이 수정 uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len) { /* USER CODE BEGIN 3 */ uint32_t i = 0; for(i = 0; i < Len; i = i + 4) { if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,(uint32_t)(dest + i),*(uint32_t *)(src + i)) == HAL_OK) { if(*(uint32_t *)(src + i) != *(uint32_t *)(dest + i)) { return 2; } } else { return 1; } } return (USBD_OK); /* USER CODE END 3 */ } | cs |
uint8_t *MEM_If_Read_FS (uint8_t *src, uint8_t *dest, uint32_t Len)함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | uint8_t *MEM_If_Read_FS (uint8_t *src, uint8_t *dest, uint32_t Len) { /* Return a valid address to avoid HardFault */ /* USER CODE BEGIN 4 */ return (uint8_t*)(USBD_OK); /* USER CODE END 4 */ } // 다음과 같이 수정 uint8_t *MEM_If_Read_FS (uint8_t *src, uint8_t *dest, uint32_t Len) { /* Return a valid address to avoid HardFault */ /* USER CODE BEGIN 4 */ uint32_t i = 0; uint8_t *psrc = src; for( i = 0; i < Len ; i++ ) { dest[i] = *psrc++; } return (uint8_t*)(USBD_OK); /* USER CODE END 4 */ } | cs |
uint16_t MEM_If_GetStatus_FS (uint32_t Add, uint8_t Cmd, uint8_t *buffer) 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | uint16_t MEM_If_GetStatus_FS (uint32_t Add, uint8_t Cmd, uint8_t *buffer) { /* USER CODE BEGIN 5 */ switch (Cmd) { case DFU_MEDIA_PROGRAM: break; case DFU_MEDIA_ERASE: default: break; } return (USBD_OK); /* USER CODE END 5 */ } // 다음과 같이 수정 uint16_t MEM_If_GetStatus_FS (uint32_t Add, uint8_t Cmd, uint8_t *buffer) { /* USER CODE BEGIN 5 */ uint16_t FLASH_PROGRAM_TIME = 50; uint16_t FLASH_ERASE_TIME = 50; switch (Cmd) { case DFU_MEDIA_PROGRAM: buffer[1] = (uint8_t)FLASH_PROGRAM_TIME; buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8); buffer[3] = 0; break; case DFU_MEDIA_ERASE: default: buffer[1] = (uint8_t)FLASH_ERASE_TIME; buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8); buffer[3] = 0; break; } return (USBD_OK); /* USER CODE END 5 */ } | cs |
'STM32F4' 카테고리의 다른 글
[STM32F4xx] Custom 보드 테스트 #23 (USB CDC.STM32F446 : KEIL) (2) | 2016.12.15 |
---|---|
[STM32F4xx] Nucleo 보드 테스트 #22 (DFU Bootloadable APP프로그램 : KEIL) (0) | 2016.12.01 |
[STM32F4xx] Nucleo 보드 테스트 #20 (DFU - Bootloader 설정 : KEIL) (1) | 2016.12.01 |
[STM32F4xx] USB 부트로더 테스트(DFU Custom Bootloader) 예정 (0) | 2016.12.01 |
[STM32F4xx] Nucleo 보드 테스트 #19 (SDIO uSD-Card 읽기.STM32F446 : KEIL) (0) | 2016.12.01 |