写一个自己的简易bootloader,能通过串口执行两条最简单的指令:
l peek addr 以一个字为单位读取内存中addr 位置的数据(addr是4字节对齐,十六进制的 形式,长度为8位十六进制,例如 0x00008000),并以十六进制的形式输出
l poke addr data 以一个字为单位修改内存中 addr 位置的数据为 data(addr 是 4 字节对齐,十六进制的形式,长度为 8位十六进制, data 也是十六进制的形式,长度为8位十六进制)
实际连接图
定义全局变量:
#define BUFFSIZE 512
#define BACKSPACE 127
#define ENTER '\r'
char str[100] = "Uart";
struct uart {
uint8_t *rear;
uint8_t *front;
};
uint8_t aRxBuffer[BUFFSIZE];
struct uart uart_rev;
aRxBuffer数组用来接收缓冲区数组,结构uart用来接收缓冲区头尾指针。
输出重定义:
void SerialPutchar(char s){
HAL_UART_Transmit(&huart1, (uint8_t*)&s, 1, 500);
}
void SerialPuts(char* s){
int i=0;
while(s[i] != '\0'){
SerialPutchar(s[i]);
i++;
}
}
void ptrInc(uint8_t **ptr, uint8_t* base, int len){
*ptr += 1;
if (*ptr >= base + len)
*ptr = base;
}
3)串口收发
串口接受采用中断接收至环形缓冲区的处理方式。生成工程后,uart_init()函数执行串口初始化,并设置中断优先级。
void uart_init(uint32_t BaudRate)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart1);
__HAL_UART_ENABLE(&huart1);
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_EnableIRQ(USART1_IRQn);
uart_rev.front = aRxBuffer;
uart_rev.rear = aRxBuffer;
if (HAL_UART_Receive_IT(&huart1, (uint8_t*)aRxBuffer, 1) != HAL_OK){
HAL_UART_Transmit(&huart1, (uint8_t*)"error_h", 7, 500);
}
}
USART1_IRQHandler()调用HAL_UART_IRQHandler()函数,处理中断。
函数末尾调用HAL_UART_Receive_IT函数,设置串口数据的存储位置,该函数会设置存储位置和接受长度,当接收的数据达到指定长度时,会进入中断回调函数 HAL_UART_RxCpltCallback() 处理中断。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
uint8_t ret = HAL_OK;
char c = *uart_rev.rear;
SerialPutchar(c);
if (c == '\r'){
SerialPutchar('\n');
}
ptrInc(&uart_rev.rear, aRxBuffer, BUFFSIZE);
if (uart_rev.rear == uart_rev.front)
ptrInc(&uart_rev.front, aRxBuffer, BUFFSIZE);
do{
ret = HAL_UART_Receive_IT(UartHandle, uart_rev.rear, 1);
}while(ret != HAL_OK);
}
void uart_init(uint32_t BaudRate)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart1);
__HAL_UART_ENABLE(&huart1);
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_EnableIRQ(USART1_IRQn);
uart_rev.front = aRxBuffer;
uart_rev.rear = aRxBuffer;
if (HAL_UART_Receive_IT(&huart1, (uint8_t*)aRxBuffer, 1) != HAL_OK){
HAL_UART_Transmit(&huart1, (uint8_t*)"error_h", 7, 500);
}
}
串口信息读取到环形缓存区存放
int8_t uart_read(uint8_t *fmt, uint16_t time_out){
while(time_out){
if(uart_rev.front != uart_rev.rear){
*fmt=*uart_rev.front;
ptrInc(&uart_rev.front, aRxBuffer, BUFFSIZE);
return 0;
}
time_out--;
}
return (int8_t)-1;
}
int8_t uart_gets(uint8_t *fmt, uint16_t upperBound)
{
int count = 0;
upperBound -= 1;
while(count < upperBound){
if(uart_rev.front != uart_rev.rear){
char c = *uart_rev.front;
ptrInc(&uart_rev.front, aRxBuffer, BUFFSIZE);
if (c == ENTER){
break;
}
*fmt = c;
if (c != BACKSPACE){
fmt++;
count++;
}else if(count > 0){
fmt--;
count--;
}
}
}
*fmt = '\0';
return count;
}
到这里,对于串口收发的函数封装完成,处理peek/poke指令。为了防止随便给poke指令的地址,崩坏程序,所以给了buff数组,并给出相应的地址及长度。
int buff[100];
buff[0] = sprintf(str, "Buffer Addr: %p Len: %d\r\n", buff, 100);
HAL_UART_Transmit(&huart1, (uint8_t*)str, buff[0], 500);
while (1) {
int count = 0;
char s[100];
char cmd[100];
SerialPuts("STM32 > ");
count = uart_gets((uint8_t*)str, 100);
sscanf(str, "%s", cmd);
if (strcmp(cmd, "peek") == 0){
int addr = 0, args = 0;
args = sscanf(str + 5, "%x %s", &addr, s);
if(args == 1){
sprintf(s, "PEEK: %x %x\r\n", addr, *((int*)addr));
SerialPuts(s);
}
else
SerialPuts("Format Error\r\n");
}else if(strcmp(cmd, "poke") == 0){
int addr = 0, args = 0, data=0;
args = sscanf(str + 5, "%x %x %s", &addr, &data, s);
if(args == 2){
*((int*)addr) = data;
sprintf(s, "POKE: %x %x\r\n", addr, *((int*)addr));
SerialPuts(s);
}
else
SerialPuts("Format Error\r\n");
}else{
SerialPuts("Instruction Error\r\n");
}
}
}
测试结果: