目錄
一、問答題
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)
}
}