SAM3X8E rt-thread移植笔记
基于Arduino DUE
September 25, 2015
目录
工贸企业有限空间作业目录特种设备作业人员作业种类与目录特种设备作业人员目录1类医疗器械目录高值医用耗材参考目录
一、前言 2
二、rt-thread简介 2
三、一步步了解系统的初始化工作 3
1、main() 3
2、rtthread_startup(); 4
3、rt_hw_board_init(); 5
4、NVIC_Configuration(); 6
5、rt_hw_usart_init() 6
6、led.c 10
四、系统测试 13
1、led控制线程 13
2、shell的使用 13
五、移植总结 14
一、前言
前一段时间买了一块Arduino DUE开发板,发现这块板子的库很匮乏,加之它是一块Cortex -M3核的单片机,所以就有给它移植操作系统的打算,本人在网上查询了很多实时系统的资料,有免费的,有开源的,最后综合考虑选择了rt-thread 这款实时系统。
二、rt-thread简介
RT-Thread(实时线程操作系统)是国内 RT-Thread 工作室精心打造的稳定的开源实时
操作系统,“她”是 RTT 核心成员历时 4 年,呕心沥血研发,力图突破国内没有小型稳定
的开源实时操作系统局面的开山之作,曾获得“第六届中日韩开源软件竞赛”技术优胜奖(其他两个技术优胜奖获得者为淘宝的OceanBase和红旗的Qomo Linux)它不仅仅是一款开源意义的硬实时操作系统(不是软的哦),也是一款产品级别的实时操作系统,目前已经被国内十多家企业采用,被证明是一款能够稳定持续运行的操作系统。
RT-Thread 实时操作系统核心是一个高效的硬实时核心,它具备非常优异的实时性、稳
定性、可剪裁性,当进行最小配置时,内核体积可以到 3k ROM 占用、1k RAM 占用。目
前 RT-thread 支持的分支和包含的组件如下
分支:
- ARM Cortex-M3: STM32F1, STM32F2, LPC176xx, LPC18xx, LM3S, EFM32, MB9BF
- ARM Cortex-M4: STM32F4, LM4S, LPC4300
- ARM7TDMI: LPC2478, LPC2148, AT91SAM7S, AT91SAM7X, S3C44B0
- ARM720T: SEP4020
- ARM9: AT91SAM9260, S3C2440
- NIOS-II
- XILINX MicroBlaze
- AVR32
- Blackfin 533
- MIPS: PIC32, Jz47xx
- PPC450: taihu
- x86
- windows simulator (VC++)
组件
- CMSIS, CMSIS-RTOS
- RT-Thread DFS 文件系统:devfs, ELM FatFs, JFFS2, NFS, romfs, UFFS, YAFFS2
- finsh shell(类似命令行的组件,RTT 的亮点哦)
- libc: armlibc(针对Keil MDK), newlib
- POSIX: pthreads, libdl
- 网络:lwIP 1.4.0
- RT-Thread GUI
- lua
- Device Drivers: IIC, MTD NOR/NAND, RTC, SDIO, serial, SPI Bus/Device, USB
device/host
RT-Thread 采用 GPL-V2 发布,并且承诺永久不会针对使用 RT-Thread 收费,用户只需要保留 RT-Thread 的 LOGO 既可以免费使用,所以这也是我当初选择它的一个重要原因。
三、一步步了解系统的初始化工作
1、main()
int main(void)
{
/* disable interrupt first */
rt_hw_interrupt_disable();
/* startup RT-Thread RTOS */
rtthread_startup();
return 0;
}
rt_hw_interrupt_disable PROC
EXPORT rt_hw_interrupt_disable
MRS r0, PRIMASK
CPSID I
BX LR
ENDP
说明:可以看到,在系统启动之前关闭了所有的中断
2、rtthread_startup();
voidrtthread_startup(void)
{
/* init board */
rt_hw_board_init();
/* show version */
rt_show_version();
#ifdef RT_USING_HEAP
#if STM32_EXT_SRAM
rt_system_heap_init((void*)STM32_EXT_SRAM_BEGIN, (void*)STM32_EXT_SRAM_END);
#else
#ifdef __CC_ARM
rt_system_heap_init((void*)&Image$$RW_IRAM1$$ZI$$Limit, (void*)STM32_SRAM_END);
#elif __ICCARM__
rt_system_heap_init(__segment_end("HEAP"), (void*)STM32_SRAM_END);
#else
/* init memory system */
rt_system_heap_init((void*)&__bss_end, (void*)STM32_SRAM_END);
#endif
#endif/* STM32_EXT_SRAM */
#endif/* RT_USING_HEAP */
/* init scheduler system */
rt_system_scheduler_init();
/* initialize timer */
rt_system_timer_init();
/* init timer thread */
rt_system_timer_thread_init();
/* init application */
rt_application_init();
/* init idle thread */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return;
}
说明:这个函数进行了系统运行的一些必要的初始化,从这个函数上可以看出需要做修改的地方在rt_hw_board_init();函数里面,这是和硬件相关的。
3、rt_hw_board_init();
voidrt_hw_board_init(void)
{
/* NVIC Configuration */
NVIC_Configuration();
/* Configure the SysTick */
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
NVIC_ClearPendingIRQ(SysTick_IRQn);
NVIC_EnableIRQ(SysTick_IRQn);
#if STM32F10X_EXT_SRAM
EXT_SRAM_Configuration();
#endif
rt_hw_usart_init();
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
}
说明:在这个函数里完成中断向量
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
设置、系统滴答时钟设置,为系统提供心跳、串口初始化,将系统输入输出终端绑定到这个串口,后续系统运行信息就会从串口打印出来。
由此可见这里的内容都需要修改,下面我按顺序解析。
4、NVIC_Configuration();
voidNVIC_Configuration(void)
{
#ifdef VECT_TAB_RAM
/* Set the Vector Table base location at 0x20000000 */
SCB->VTOR = IRAM0_ADDR | (0X0 & (uint32_t)0x1FFFFF80);
#else/* VECT_TAB_FLASH */
/* Set the Vector Table base location at 0x08000000 */
// #define NVIC_VectTab_FLASH ((uint32_t)0x08000000)
SCB->VTOR = IFLASH0_ADDR | (0X0 & (uint32_t)0x1FFFFF80);
#endif
}
说明:这里设置中断向量表的起始位置,可以设置到ARM,也可以设置到FLASH
#define NVIC_VectTab_FLASH ((uint32_t)0x08000000)STM32的FLASH起始地址。
#define IFLASH0_ADDR (0x00080000u)SAM3X8E的FLASH0起始地址,可以从芯片手册查阅,也可以在sam3x8e.h头文件中找到。
其实flash0 和flash1的地址空间是连续的,但是SRAM0和SRAM1的地址空间不连续
5、rt_hw_usart_init()
#defineinline__inline
#include"sam.h"
#include"chip.h"
#include"boardusart.h"
#include"usart.h"
#include"board.h"
#include
/* SAM3 uart driver */
structSAM3_uart
{
Uart* uart_device;
IRQn_Typeirq;
};
voidUSART_ITConfig(Uart *serial, uint16_t USARTIT_RXNE, FunctionalStatecmd)
{
}
staticrt_err_t sam3_configure(structrt_serial_device *serial, structserial_configure *cfg)
{
return RT_EOK;
}
staticrt_err_t sam3_control(structrt_serial_device *serial, intcmd, void *arg)
{
struct SAM3_uart* uart;
RT_ASSERT(serial != RT_NULL);
uart = (struct SAM3_uart *)serial->parent.user_data;
switch (cmd)
{
/* disable interrupt */
case RT_DEVICE_CTRL_CLR_INT:
/* disable rxirq */
UART_DISABLE_IRQ(uart->irq);
/* disable interrupt */
USART_ITConfig(uart->uart_device, USART_IT_RXNE, DISABLE);
break;
/* enable interrupt */
case RT_DEVICE_CTRL_SET_INT:
/* enable rxirq */
UART_ENABLE_IRQ(uart->irq);
/* enable interrupt */
USART_ITConfig(uart->uart_device, USART_IT_RXNE, ENABLE);
break;
}
return RT_EOK;
}
staticint sam3_putc(structrt_serial_device *serial, char c)
{
struct SAM3_uart* uart;
RT_ASSERT(serial != RT_NULL);
uart = (struct SAM3_uart *)serial->parent.user_data;
while ((UART->UART_SR & UART_SR_TXEMPTY) == 0);
//UART->UART_THR=c;
uart->uart_device->UART_THR = c;
return 1;
}
staticint sam3_getc(structrt_serial_device *serial)
{
intch;
struct SAM3_uart* uart;
RT_ASSERT(serial != RT_NULL);
uart = (struct SAM3_uart *)serial->parent.user_data;
ch = -1;
if (uart->uart_device->UART_SR & US_CSR_RXRDY)
{
ch = uart->uart_device->UART_RHR & 0xff;
}
returnch;
}
staticconststructrt_uart_ops sam3_uart_ops =
{
sam3_configure,
sam3_control,
sam3_putc,
sam3_getc,
};
#ifdefined(RT_USING_UART1)
/* UART1 device driver structure */
struct SAM3_uart SAM_UART =
{
UART,
UART_IRQn
};
structrt_serial_device serial0;
voidUART_IRQHandler(void)
{
struct SAM3_uart* uart;
uart = &SAM_UART;
/* enter interrupt */
rt_interrupt_enter();
if ((UART->UART_SR& USART_IT_RXNE) != RESET)
{
rt_hw_serial_isr(&serial0, RT_SERIAL_EVENT_RX_IND);
}
sam3_getc(&serial0);
/* leave interrupt */
rt_interrupt_leave();
}
#endif/* RT_USING_UART0 */
/*UART配置*/
staticvoidUART_Configuration(void)
{
/*开启PIO时钟 */
pmc_enable_periph_clk(ID_PIOA);
/*开启PIOA的寄存器*/
// PMC->PMC_PCER0=1<PIO_OER |= (1 << 9);//输出使能(PA9输出)
PIOA->PIO_PER |= (1 << 9);
//PIOA->PIO_MDER|=(1<<9) ;//复用功能
PIOA->PIO_PUER |= (1 << 9);//推挽输出
PIOA->PIO_IFER |= (1 << 8);//输入
PIO_SetPeripheral(PIOA, PIO_PERIPH_A, PIO_PA9 | PIO_PA8);
//PIO_Configure(PIOA,PIO_PERIPH_A,PIO_PA9,);
//pmc_enable_periph_clk( ID_UART );
PMC->PMC_PCER0 = 1 << ID_UART;
/* Reset and disable receiver and transmitter*/
UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
/*Configure mode*/
UART->UART_MR = UART_MR_PAR_NO;
/*Configure baudrate*/
UART->UART_BRGR = US_BRGR_CD(46);
// Configure interrupts
UART->UART_IDR = 0xFFFFFFFF;
UART->UART_IER = UART_IER_RXRDY | UART_IER_OVRE | UART_IER_FRAME;
// Enable US interrupt in NVIC
NVIC_EnableIRQ(ID_UART);
NVIC_SetPriority(ID_UART, 0 & 0x0F);
/* Disable PDC channel */
UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
// Enable receiver and transmitter
UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
}
voidrt_hw_usart_init(void)
{
structSAM3_uart* uart;
structserial_configureconfig = RT_SERIAL_CONFIG_DEFAULT;
UART_Configuration();
uart = &SAM_UART;
config.baud_rate = BAUD_RATE_115200;
serial0.ops = &sam3_uart_ops;
serial0.config = config;
/* register UART device */
rt_hw_serial_register(&serial0, "uart1",
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
uart);
}
说明:这这个函数里实现对终端所用串口的绑定,原始代码已被删除,这个文件是我重写的,主要是对串口uart的初始化化,串口收发函数的实现,串口Irq的处理,具体的寄存器配置需要阅读芯片手册。
为了能够显示系统的工作状态,还需要对板载的LED进行配置。通过查看电路原理图,可以知道板载LED连接在PIO_PB27上,下面对led的配置与操作函数进行讲解
6、led.c
/*
* File : led.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2009, RT-Thread Development Team
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
*
*
* Change Logs:
* Date Author Notes
* 2009-01-05 Bernard the first version
*/
#include
#include
#include"led.h"
// led define
#defineled1_pin (PIO_PB27)
voidrt_hw_led_init(void)
{
PMC->PMC_WPMR = 0x504D4300; /* Disable write protect */
PMC->PMC_PCER0 = 1UL << ID_PIOB; /* enable PIOB clock */
PIOB->PIO_PER =
PIOB->PIO_OER =
PIOB->PIO_PUDR =
PIOB->PIO_OWER = led1_pin; /* Setup PIO_PB27 for LEDs */
PMC->PMC_WPMR = 0x504D4301; /* Enable write protect */
rt_hw_led_off(0); /* switch LEDs off */
}
voidrt_hw_led_on(u8 n)
{
switch (n)
{
case 0:
PIOB->PIO_SODR = led1_pin;
break;
case 1:
break;
default:
break;
}
}
voidrt_hw_led_off(u8 n)
{
switch (n)
{
case 0:
PIOB->PIO_CODR = led1_pin;
break;
case 1:
break;
default:
break;
}
}
#ifdef RT_USING_FINSH
#include
static rt_uint8_t led_inited = 0;
void led(rt_uint32_t led, rt_uint32_t value)
{
/* init led configuration if it's not inited. */
if (!led_inited)
{
rt_hw_led_init();
led_inited = 1;
}
if (led == 0)
{
/* set led status */
switch (value)
{
case 0:
rt_hw_led_off(0);
break;
case 1:
rt_hw_led_on(0);
break;
default:
break;
}
}
if (led == 1)
{
/* set led status */
switch (value)
{
case 0:
rt_hw_led_off(0);
break;
case 1:
rt_hw_led_on(1);
break;
default:
break;
}
}
}
FINSH_FUNCTION_EXPORT(led, set led[0 - 1] on[1] or off[0].)
#endif
说明:由于Arduinio DUE板载的led只有一个 所以就只使用了case 0
四、系统测试
1、led控制线程
staticvoidled_thread_entry(void* parameter)
{
/* Configure the LED PB.27 */
rt_hw_led_init();
while (1)
{
/* led1 on */
rt_hw_led_on(0);
rt_thread_delay(RT_TICK_PER_SECOND * 1);
/* led1 off */
rt_hw_led_off(0);
rt_thread_delay(RT_TICK_PER_SECOND * 1);
}
}
2、shell的使用
端口根据自己的电脑选择,波特率为115200
图1 Shell启动
图2 显示全部shell命令
图3执行list_mem()命令
五、移植总结
经过了断断续续一个星期总算把rt-thread移植成功了,对于这个系统的底层实现细节我还不清楚。移植一个实时系统我理解为两种情况:相同架构间的移植,不同架构间的移植。比如我这次移植rt-thread其实移植了两个版本,第一个版本是在stm32f103c8t6上移植,第二个版本是在SAM3X8E上移植。
第一个版本移植很简单,官方给了一个stm32f103zet6的rt-thread例子。我自己制作的板子是stm32f103c8t6,移植起来超简单,只需要修改系统配置文件中关于SRAM的其实地址和SRAM大小,然后在工程中设置微控制器的型号,及其它微处理器相关的参数即可,整个移植过程不到10分钟。这种移植是属于同系列微控制器间移植,是最简单的移植。
第二个版本的移植就比较麻烦了,我这次是从stm32f103zet6向SAM3X8E上移植。由于STM32F103ZET6中对片上外设的配置是在官方库上完成的,所有就简单了不少,但是Atmel公司并没有给SAM3X8E在keil下的库,它有自己的AtmelStudio集成开发环境基于gcc编译器,这个开发环境下有ASF库。最开始我把ASF库移植到keil环境下,但是由于编译器的原因,这个库在keil下并不能正常工作,最后只好作罢。
Atmel公司虽然没有给keil下的库,但是给出了相关的寄存器定义,片上外设结构体的定义(不得不说用结构体来配置外设的确是个好方法,由于C语言结构体在地址空间上是连续的,所以只需要给结构体的地址设为外设的起始地址即可,而与外设相关的寄存器都是相对于这个起始地址的偏移地址,这和结构体是一样的。所以用结构体配置外设非常的方便)。我先去查看电路原理图,知道了SAM3X8E用于编程的串口是UART,这个和STM32不一样,STM32用的是USART1。然后就先办法让SAM3X8E的UART工作起来。这里我又犯了一个错误,我是直接在rt-thread系统下配置UART的,搞了两天也没有搞好,网上关于SAM3X的资料太少,用它来玩电子积木的也不多。所以建议初学者不要入手这块板子,其它的avrArduino板可玩性更强。
我昨天晚上索性新建了个工程用了测试uart,没用多长时间就搞好了,先后实现了查询法和中断法的串口通讯。接下来就向rt-thread移植这部分代码,仿照STM32的例子,它用库函数实现的功能,我对照着数据手册,用操作寄存器的方式实现,所以也精简了大量的代码,最终实现了在putty终端使用rt-thread的shell。
对于这种相同架构不同厂家的移植稍微麻烦一点,但是只要有经验也是很快的。不需要修改任务调度相关的汇编代码,因为都是使用Thumb2指令。
不同架构微控制器间的移植要更麻烦些了,需要对汇编代码进行修改。
这次的移植还有一点小遗憾,就是SRAM并没有被系统全部管理。我的这块板子使用的SAM3X8E SRAM分为两部分64K+32K且不连续(还有4K用于USB和CAN)。在rt-thread的配置头文件中并没有对这种情况的处理,它的配置为:SRAM的起始地址+SIZE。所以目前就只管理了64K的空间,不知道是不是需要修改内存管理算法实现全部内存的管理,还是通过编译器来实现,或者别的方法。
图4 通过终端打印的内存使用情况