基于AT32的RT-Thread移植
准备源码,配置工程
仅供有移植RTOS基础的人参考借鉴。
添加源码
直接用CubeMX生成的RT-Thread_Nano源码,复制到自己的AT32工程内里。
AT32大多芯片是M4架构,注意替换libcpu中的文件。注意:AT32F437ZGT7是M4架构
把RTT源码添加进keil工程
开启SysTick中断。
添加头文件路径
屏蔽那几个中断
SVC_Handler、PendSV_Handler(void)、SysTick_Handler(void)、HardFault_Handler(void),因为这些函数RT-Thread已经帮我们实现好了。
修改部分.c/.h
根据板子重新实现特定函数
修改board.c中的rt_hw_board_init。如果需要使用RT-Thread的shell命令行调试工具,还需要修改uart_init(void)、rt_hw_console_output、rt_hw_console_getchar这几个函数。
多数情况下还需要在串口中断函数中手动清除下接收完成标志位。
rt_hw_board_init:
这个作用主要就是初始化时钟,关键是要开启SysTick中断。
/*** This function will initial your board.*/
void rt_hw_board_init(void)
{/* * 1: OS Tick Configuration* Enable the hardware timer and call the rt_os_tick_callback function* periodically with the frequency RT_TICK_PER_SECOND. *//* 添加的代码部分begin */system_clock_config(); // 已正确配置HCLK=288MHzSysTick_Config(288000-1);/* * 设置SysTick中断优先级:* AT32使用4位优先级(0~15),值越大优先级越低* 设为15(最低),避免影响高优先级外设中断*/NVIC_SetPriority(SysTick_IRQn, 15);/* 添加的代码部分end *//* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INITrt_components_board_init();
#endif#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
rt_hw_console_output
void rt_hw_console_output(const char *str)
{rt_size_t i = 0;rt_size_t size = rt_strlen(str);char a = '\r'; // 换行前添加回车for (i = 0; i < size; i++){// 遇到换行符时先发送回车if (*(str + i) == '\n'){// 等待发送缓冲区为空while (usart_flag_get(CONSOLE_USART, USART_TDBE_FLAG) == RESET);usart_data_transmit(CONSOLE_USART, a);}// 发送当前字符while (usart_flag_get(CONSOLE_USART, USART_TDBE_FLAG) == RESET);usart_data_transmit(CONSOLE_USART, *(str + i));}// 等待最后一个字符发送完成while (usart_flag_get(CONSOLE_USART, USART_TDC_FLAG) == RESET);
}
USART1_IRQHandler和rt_hw_console_getchar
// 接收缓存:-1表示无数据
static volatile int usart_rx_buf = -1;
// 接收完成标志
static volatile rt_bool_t usart_rx_ready = RT_FALSE;/*** USART中断处理函数:接收数据并缓存*/
void USART1_IRQHandler(void)
{rt_interrupt_enter();// 处理接收缓冲区非空中断(AT32标志位为USART_RDBF_FLAG)if (usart_flag_get(CONSOLE_USART, USART_RDBF_FLAG) == SET){// 读取接收数据并缓存usart_rx_buf = (int)(usart_data_receive(CONSOLE_USART) & 0xFF);usart_rx_ready = RT_TRUE;// 清除标志位(AT32需要手动清除)usart_flag_clear(CONSOLE_USART, USART_RDBF_FLAG);}rt_interrupt_leave();
}char rt_hw_console_getchar(void)
{int ch = -1;// 等待中断接收数据while (usart_rx_ready != RT_TRUE){rt_thread_mdelay(1); // 短延时等待,降低CPU占用}// 读取缓存数据并重置标志ch = usart_rx_buf;usart_rx_ready = RT_FALSE;usart_rx_buf = -1;return ch;
}
board.c。仅供参考
/** Copyright (c) 2006-2019, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2021-05-24 the first version*/#include <rthw.h>
#include <rtthread.h>//#include "main.h"
#include "at32f435_437.h"
#include "at32f435_437_clock.h"
#include "usart.h"#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
/** Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP* the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes*/
#define RT_HEAP_SIZE (15*1024)
static rt_uint8_t rt_heap[RT_HEAP_SIZE];RT_WEAK void *rt_heap_begin_get(void)
{return rt_heap;
}RT_WEAK void *rt_heap_end_get(void)
{return rt_heap + RT_HEAP_SIZE;
}
#endifvoid SysTick_Handler(void)
{rt_interrupt_enter();rt_tick_increase();rt_interrupt_leave();
}#ifdef RT_USING_FINSH
#include <finsh.h>
static void reboot(uint8_t argc, char **argv)
{rt_hw_cpu_reset();
}
MSH_CMD_EXPORT(reboot, Reboot System);
#endif /* RT_USING_FINSH *//*** This function will initial your board.*/
void rt_hw_board_init(void)
{/* * 1: OS Tick Configuration* Enable the hardware timer and call the rt_os_tick_callback function* periodically with the frequency RT_TICK_PER_SECOND. *//* 添加的代码部分begin */system_clock_config(); // 已正确配置HCLK=288MHzSysTick_Config(288000-1);/* * 设置SysTick中断优先级:* AT32使用4位优先级(0~15),值越大优先级越低* 设为15(最低),避免影响高优先级外设中断*/NVIC_SetPriority(SysTick_IRQn, 15);/* 添加的代码部分end *//* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INITrt_components_board_init();
#endif#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}// 定义控制台使用的USART端口(根据实际硬件调整)
#define CONSOLE_USART USART1
#ifdef RT_USING_CONSOLE//static UART_HandleTypeDef UartHandle;
static int uart_init(void)
{/* TODO: Please modify the UART port number according to your needs */usart_config();return 0;
}
INIT_BOARD_EXPORT(uart_init);void rt_hw_console_output(const char *str)
{rt_size_t i = 0;rt_size_t size = rt_strlen(str);char a = '\r'; // 换行前添加回车for (i = 0; i < size; i++){// 遇到换行符时先发送回车if (*(str + i) == '\n'){// 等待发送缓冲区为空while (usart_flag_get(CONSOLE_USART, USART_TDBE_FLAG) == RESET);usart_data_transmit(CONSOLE_USART, a);}// 发送当前字符while (usart_flag_get(CONSOLE_USART, USART_TDBE_FLAG) == RESET);usart_data_transmit(CONSOLE_USART, *(str + i));}// 等待最后一个字符发送完成while (usart_flag_get(CONSOLE_USART, USART_TDC_FLAG) == RESET);
}
#endif#ifdef RT_USING_FINSH// 接收缓存:-1表示无数据
static volatile int usart_rx_buf = -1;
// 接收完成标志
static volatile rt_bool_t usart_rx_ready = RT_FALSE;/*** USART中断处理函数:接收数据并缓存*/
void USART1_IRQHandler(void)
{rt_interrupt_enter();// 处理接收缓冲区非空中断(AT32标志位为USART_RDBF_FLAG)if (usart_flag_get(CONSOLE_USART, USART_RDBF_FLAG) == SET){// 读取接收数据并缓存usart_rx_buf = (int)(usart_data_receive(CONSOLE_USART) & 0xFF);usart_rx_ready = RT_TRUE;// 清除标志位(AT32需要手动清除)usart_flag_clear(CONSOLE_USART, USART_RDBF_FLAG);}rt_interrupt_leave();
}char rt_hw_console_getchar(void)
{int ch = -1;// 等待中断接收数据while (usart_rx_ready != RT_TRUE){rt_thread_mdelay(1); // 短延时等待,降低CPU占用}// 读取缓存数据并重置标志ch = usart_rx_buf;usart_rx_ready = RT_FALSE;usart_rx_buf = -1;return ch;
}
#endif
main.c测试代码
#include "at32f435_437_clock.h"
#include "./BSP/led.h"
#include "./BSP/key.h"
#include "./BSP/beep.h"
#include "Delay.h"
#include "./BSP/usart.h"#include <rthw.h>
#include "rtthread.h"
#include <stdio.h>/* 定义线程控制块 */
static rt_thread_t led1_thread = RT_NULL;static void led1_thread_entry(void *parameter)
{while (1){rt_thread_delay(500); /* 延时500个tick */gpio_bits_set(LED2_GPIO,LED2_PIN);rt_thread_delay(500); /* 延时500个tick */gpio_bits_reset(LED2_GPIO,LED2_PIN); }
}int main(void)
{/* 初始化系统时钟 */system_clock_config();/* 初始化引脚定义 */led_init();key_init(); beep_init();usart_config();/* 间隔1s发送一次AT32_TEST */
// printf("AT32_USART测试\r\n");// 在main函数中添加
// rt_kprintf("SystemCoreClock: %d Hz\n", SystemCoreClock); // 应输出288000000
// rt_kprintf("SysTick时钟: %d Hz\n", (SysTick->CTRL & SysTick_CTRL_CLKSOURCE_Msk) ? SystemCoreClock : (SystemCoreClock/8));
// led1_thread = /* 线程控制块指针 */rt_thread_create("led1", /* 线程名字 */led1_thread_entry, /* 线程入口函数 */RT_NULL, /* 线程入口函数参数 */128, /* 线程栈大小 */20, /* 线程的优先级 */20); /* 线程时间片 *//* 启动线程,开启调度 */rt_thread_startup(led1_thread);while(1){rt_thread_delay(1000);}
}
可能遇到的问题以及可尝试
卡死在空闲线程。
1. 可能调度器未开启,原因是SysTick中断未正常运行。
shell命令行无反应
1. 串口中断函数的接收标志位是否有清除,如果不清除会一直卡在串口中断函数。