目录
一、问答题
1、RT-Thread的启动流程
整个启动过程为:startup_xx.s ————> rt-thread启动 ————>main。在rt-thread启动中大概有以下四个部分:
① 初始化与系统相关的硬件
② 初始化系统内核对象,如定时器、调度器、信号
③ 创建main线程,在main线程中依次对各类模块进行初始化
④ 初始化定时器线程、空闲线程,并启动调度器
2、加载时地址与运行时地址映射
image文件
STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
分散装载配置文件里会有配置,关于code的地址,有两个设置,一个是存储地址(这个地址配置的是烧写器把代码段写到flashrom的何处),一个是装载运行地址,也就是你程序在什么地方运行
3、MDK环境下各种数据段存储的什么数据
- code:代码段,存放程序
- RO:只读数据段,存放程序中定义的常量
- RW:读写数据段,存放非0全局变量
- ZI:0数据段,存放未初始化的全局变量与初始化为0的变量
MDK 在编译完成之后
Total RO Size (Code + RO Data) 53668 ( 52.41kB)
Total RW Size (RW Data + ZI Data) 2728 ( 2.66kB)
Total ROM Size (Code + RO Data + RW Data) 53780 ( 52.52kB)
- 1)RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
- 2)RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
- 3)ROM Size 包含了 Code、RO Data 以及 RW Data,表示烧写程序所占用的 Flash 空间的大小;
4、自动初始化原理
rt-thread的知道初始化机制使用了自定义的RTI符号段,将需要在启动时初始化的函数指针放到该段中,形成一张知道初始化函数表,在系统启动过程中会遍历该表,并调用该表中的函数,达到自动初始化的目的。用来实现自动初始化功能的宏定义接口如下:
初始化顺序 |
宏接口 |
描述 |
1 |
INIT_BOARD_EXPORT(fn) |
非常早期的初始化,此时调度器还未启动 |
2 |
INIT_PREV_EXPORT(fn) |
主要用于纯软件,没有太多依赖的函数 |
3 |
INIT_DEVICE_EXPORT(fn) |
外设驱动初始化相关,比如网卡驱动 |
4 |
INIT_COMPONENT_EXPORT(fn) |
组件初始化,比如文件系统或者LWIP |
5 |
INIT_ENV_EXPORT(fn) |
系统环境初始化,比如挂载文件系统 |
6 |
INIT_APP_EXPORT(fn) |
应用初始化,比如GUI应用 |
5、文件系统的使用流程
1、初始化DFS组件:dfs_init()
2、初始化具体的文件系统:并将具体的文件系统祖册到DFS中:
elm_init() ——> dfs_register(&dfs_elm)
3、在存储器上创建块设备,并将块设备祖册到I/O设备管理器中:rt_sfud_flash_probe()
4、在块设备上创建指定的文件系统,即格式化文件系统:dfs_mkfs("elm", "sd0")
5、挂载块设备到DFS目录中,即挂载文件系统:dfs_mount("sd0", "/", "elm", 0, 0)
6、STM32 BSP的制作过程
1、复制通用模板:将bsp/stm32/libraries/templates 下的通用模板复制到bsp/stm32下
2、使用CUBMX配置工程,生成stm32fxx_hal_msp.c/.h:选择芯片型号、打开外部时钟、选择下载方式、打开串口外设、配置系统时钟
3、修改board.c中的系统时钟初始化函数,修改board.h中的FLASH和RAM大小
4、修改kconfig选项:修改芯片型号与系列
5、修改连接脚本中的FLASH和RAM大小
6、修改sconscript构建脚本,修改芯片型号和启动文件地址
7、修改过程模板,重新生成过程
7、I/O设备管理框架层的意义
使设备的硬件操作与应用程序相互独立,双方只需关心各自的实现,从而降低代码的耦合性、复杂性、提高了系统的稳定性
8、RAM处理器寄存器
R0-R12:通用寄存器
R13:主堆栈指针(MSP)、进程堆栈指针(PSP)
R14:连接寄存器(LR)
R15:程序计数器(PC)
9、RTOS和前后台系统
1、前后台系统是指:包括一个死循环的应用程序的后台系统,和若干个中断服务程序的前台系统
2、RTOS分为硬实时和软实时,硬实时要求在规定的时间内完成必须完成的操作
二、编程题
1、使用信号量的方式,同步LED的亮灭,500ms亮 500ms灭。创建两个线程,线程1使用信号量定时通知LED的亮灭;线程2根据通知执行LED的亮灭
#define LED_PIN GET_PIN(F, 9)
rt_sem_t led_sem;
static void sem_entry(void *parameter)
{
while(1)
{
rt_sem_release(led_sem);
rt_thread_mdelay(500);
}
}
static void sem_entry(void *parameter)
{
static unsigned char cnt = 0;
while(1)
{
rt_sem_take(led_sem, RT_WAITING_FOREVER);
if(cnt++ % 2)
rt_pin_write(LED_PIN, PIN_HIGH);
else
rt_pin_write(LED_PIN, PIN_LOW);
}
}
int led_sample(void)
{
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
led_sem = rt_sem_creat("led_sem", 1, RT_IPC_FLAG_FIFO)
if (led_sem == RT_NULL)
{
rt_kprintf("creat led sem fail!\n");
return -RT_ERROR;
}
tid1 = rt_thread_creat("ctl_sem",sem_entry,RT_NULL,512,10,0);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
tid2 = rt_thread_creat("ctl_sem",led_entry,RT_NULL,512,d11,0);
if (tid2 != RT_NULL)
rt_thread_startup(tid2 );
}
INIT_APP_EXPORT(led_sample, led dample)
2、有一系列文件如:1.txt, 12.txt, 123.txt,从中找出1.txt,并将文件内容输出出来。
static void findfile_sample(void)
{
DIR *dirp;
struct dirrnt *d;
char *f;
char buffer[100];
/*打开根目录*/
dirp = opendir("/");
if (dirp == RT_NULL)
{
rt_kprintf("open directory error\n");
}
else
{
while ((d = readdir(dirp) != RT_NULL))/*读取目录*/
{
f = d->d_name;
if(!strcmp(f, "1.txt"))
{
fd = open("1.txt", O_RDONLY);
if (fd >= 0)
{
read(fd, buffer, sizeof(buffer));
rt_kprintf("file 1.txt was found, the content is %s", buffer)
close(fd);
}
}
}
closedir(dirp);
}
}
MSH_CMD_EXPORT(findfile_sample, find file)
3、创建一个名为rtthread.txt的文本文件,并写入“hello world”到文件中,然后读出打印
void write_read_sample(void)
{
int fd;
char *s = "hello world\n";
char buffer[100] = {0};
fd = open("/rtthread.txt", O_WRONLY | O_CREAT)
if (fd >= 0)
{
write(fd, s, sizeof(s));
close(fd)
}
fd = open("/rtthread.txt", O_RDONLY)
if(fd >= 0)
{
size = read(fd, buffer, sizeof(buffer))
close(fd);
}
}
MSH_CMD_EXPORT(write_read_sample)
4、tcp客户端
struct sockaddr
{
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
/* members are in network byte order */
struct sockaddr_in
{
uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
#define SIN_ZERO_LEN 8
char sin_zero[SIN_ZERO_LEN];
};
struct hostent {
char *h_name; /* Official name of the host. */
char **h_aliases; /* A pointer to an array of pointers to alternative host names,
terminated by a null pointer. */
int h_addrtype; /* Address type. */
int h_length; /* The length, in bytes, of the address. */
char **h_addr_list; /* A pointer to an array of pointers to network addresses (in
network byte order) for the host, terminated by a null pointer. */
#define h_addr h_addr_list[0] /* for backward compatibility */
};
struct in_addr
{
in_addr_t s_addr;
};
#define BUFSZ 100
int port = 8080;
void tcp_client(void *url)
{
char *rdata;
int rdata_bytes;
int sock = -1;
struct hostent *host = RT_NULL;
struct sockaddr_in server_addr;
struct timeval timeout;
fd_set readset;
/*地址解析*/
host = gethostbyname(url)
if (host == RT_NULL)
return;
/*分配内存接受数据*/
rdata = rt_malloc(BUFSZ);
if (rdata == RT_NULL)
return;
/*创建socket*/
sock = socket(AF_INET, SOCKET_STREAM, 0);
if (sock == -1)
goto _exit;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_sddr = *((struct in_addr *)host->h_addr);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/*连接到服务器*/
if (connect(sock, (struct *)&server_addr, sizeof(struct sockaddr)) == -1)
goto _exit;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
while(1)
{
FD_ZERO(&readset);
FD_SET(sock, &readset);
if (select(sock+1, &readset, RT_NULL, RT_NULL, &timeout) == 0)
continue;
rdata_bytes = recv(sock, rdata, BUFSZ-1, 0)
if (rdata_bytes < 0)
{
goto _exit;
}
else if(rdata_bytes == 0)
{
continue;
}
else
{
rdata[rdata_bytes ] = '\0';
if (rt_strcmp(rdata, "q") == 0 || rt_strcmp(rdata, "Q") == 0)
goto _exit;
else
LOG_D("receive data is %s\n", rdata);
}
ret = send(sock, send_data, rt_strlen(send_data), 0)
if (ret < 0)
goto _exit;
else if(ret == 0)
}
}