특히 부트코드는 최대한 가볍고 짧게 작성하는 것이 좋음.
필요한 라이브러리인 Flash, UART 관련 라이브러리만 작성하고 나머지는 지우는 것이 좋음.
<부트로더>
임베디드 보드 전원 -> 플래시 메모리(ROM)에서 시스템 소프트웨어 시작(H/W 초기화)->리눅스커널을 메모리(RAM)에 적재=사용자명령처리준비
<플래시 사이즈 측정>
사용하고 있는 mcu는 STM32F100x6. 따라서 데이터시트의 Device overview를 살펴보면, 아래와 같은표가 나온다.
Memory mapping은 사이즈가 큰 mcu기준으로 그려지기 때문에 그 그림을 참고하면 안됨!
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 |