F💻W/Coding

[STM32F030C6T6] Bootloader 구현

천숭이 2023. 3. 30. 10:17

 

특히 부트코드는 최대한 가볍고 짧게 작성하는 것이 좋음.

 

필요한 라이브러리인 Flash, UART 관련 라이브러리만 작성하고 나머지는 지우는 것이 좋음.

 

<부트로더>

임베디드 보드 전원 -> 플래시 메모리(ROM)에서 시스템 소프트웨어 시작(H/W 초기화)->리눅스커널을 메모리(RAM)에 적재=사용자명령처리준비

 

<플래시 사이즈 측정>

사용하고 있는 mcu는 STM32F100x6. 따라서 데이터시트의 Device overview를 살펴보면, 아래와 같은표가 나온다.

Memory mapping은 사이즈가 큰 mcu기준으로 그려지기 때문에 그 그림을 참고하면 안됨!

 

Device overview

 

STM32F100Cx 에서 4열은 아래 그림처럼 세부 항목에 대한 순서이다. C6에 해당하므로 두번째 열을 참고하기!

 

 

수정이 필요한 인터럽트 함수 - 시리얼로 오는 데이터를 넘겨주고 바로 clear해주는 작업

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint32_t tmp_flag = 0, tmp_it_source = 0;
	tmp_flag = __HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE);
	tmp_it_source = __HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE);
	if ((tmp_flag != RESET) && (tmp_it_source != RESET)) { 
	  HAL_UART_Receive(&huart1, USART_BUF, 1, 100);
	}
	__HAL_UART_CLEAR_PEFLAG(&huart1);

  /* USER CODE END USART1_IRQn 0 */
	 HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

 

수정이 필요한 인터럽트 함수2 - uart enable 함수를 반드시 작성해줘야 한다

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN USART1_MspInit 1 */
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); 
  /* USER CODE END USART1_MspInit 1 */
  }

}

 

수정이 필요한 설정

점프할 주소가 0x08002800이므로, 어플리케이션 프로젝트의 system 파일에서(Boot프로젝트 아님!!) 플래시의 백터테이블 오프셋을 2800으로 변경해줘야 한다.
그리고 #define USER_VECT_TAB_ADDRESS 부분을 주석 해제 해야한다 (진짜 완전 중요 ☆★)

 

 

 

코드

<main.c>

int main(void)
{  
  HAL_FLASH_Unlock();
  
  InternalFlash_Page1024Read(0x08007C00);       // 메모리의 끝 - 1K
  

  if(InterFlash[1023] == 0xAA){     
   	--system init 함수들 작성 --
    
    while(1){
		-- while 루프 내 동작 작성 --
    }
  }
  else{                                // 메모리 끝이 0xFF 일 때, JUMP TO APP
    if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000) == 0x20000000)
    {
      /* Jump to user application */ // Application 주소가 0x08002800으로 설정된 상태
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) 0x08002800);
      Jump_To_Application();
    }
  }
  while (1);
}

 

< 함수 CommandFunctions() >

void CommandFunctions(void)
{
  uint8_t buff[3];
  uint16_t i;
  uint8_t ptr = 0;
  

        HAL_FLASH_Unlock();

        InterFlashAddr = (0x08002800 +(u32)(SYS_RxBuf[0]*1024));
        memset(InterFlash, 0, 1024);
        memcpy(InterFlash, &Buffer[1], 1024);                // 버퍼 내용을 InterFlash에 복사
        
        InternalFlash_Page1024Write(InterFlashAddr);            //InterFlash 데이터 플래쉬 주소에 write

        memset(InterFlash, 0, 1024);                            // InterFlash 0으로 초기화
        InternalFlash_Page1024Read(InterFlashAddr);             // 
        check = 0;
        for(i=0;i<1024;i++){
          if(InterFlash[i] == Buffer[i+1]) check += 0;
          else check += 1;
        }
        if(check == 0){
          SEND_ACK();      // ACK를 보냄으로써 동작 확인
        }
        else SEND_NACK();  // 그 반대 NACK
       }
       
       else if (SYS_Com == 3) {
         memset(InterFlash, 0xFF, 1024);                        // 메모리 끝부분 FF로 쓰기
         InternalFlash_Page1024Write(0x08007C00);
         HOST_ACK();
         _delay_ms(500);
         
         HAL_NVIC_SystemReset();                                    // mcu리셋, 부트모드 재진입
       }
     }
     
  }
}

 

< InterFlash_Page1024 Read & Write() >

u8 InternalFlash_Page1024Read(u32 addr)
{
  u32 i;
  u32 ramdata;
  
  ramdata = (u32)InterFlash;
  for(i=0;i<256;i++){
    *(__IO uint32_t *)(ramdata) = *(__IO uint32_t *)(addr);
    addr += 4;
    ramdata += 4;
  }
  return 1;
}


u8 InternalFlash_Page1024Write(u32 addr)
{
  u32 i;
  u32 ramdata;
  
  PAGEError = 0;
  HAL_FLASH_Unlock();                                   // 플래시 메모리 조작을 위한 플래시 Unlock

  EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
  EraseInitStruct.PageAddress = addr;                   // 부트 메모리의 끝 주소-1K
  EraseInitStruct.NbPages     = 1;
  
  HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError); 
  
  ramdata = (u32)InterFlash;
  for(i=0;i<256;i++){
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *(__IO uint32_t *)(ramdata));     // ramdata 형변환 필수
    addr += 4;
    ramdata += 4;
  }
  return 1;
}

 

 


<에러 해결 과정>

 

1. *(u32*) 자료형을 -> *(__IO uint32_t*) 로 형변환

2. ‘FF’를 마지막에 써주는 작업에서 HOST에게 ACK 응답을 보내야한다
 -> JND USB통신을 위해 만들어진 GUI라 ACK를 보내야하는 시스템으로 설계된 것 같다고 유추하심

3. HAL 함수로 모두 대체하고, Erase관련 구조체 EraseInitstruct 을 선언하고 그에 맞는 매개변수 작성

EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = addr;   // Jump할 address + SYS_RxBuf[0]*1024      
EraseInitStruct.NbPages     = 1;

'F💻W > Coding' 카테고리의 다른 글

Atmega로 UART, SPI 통신 구현  (0) 2023.12.04
ATMEGA UART 출력  (0) 2023.11.21
gpio mode 참고  (0) 2023.07.10
Silicon Lab EFR32 코드 분석  (0) 2023.06.26
JSON Encoding/Decoding in C  (0) 2022.10.18
손가락 제스처(1~5) 학습 및 테스트 -> 라즈베리파이 에러  (0) 2022.07.30