目录
1.rtthread内核框架与线程调度介绍
2.rtthread内核功能启动流程及汇编阶段
3.rtthread内存分布
4.内核对象管理系统
5.内核配置和剪裁
6.线程5种状态
7.进程与线程
8.CPU的内部结构
9.中断的优缺点
10.GPIO的输入输出模式
11.tcp为什么需要3次握手?第3次握手去掉可以不?
12.系统定时器初始化简介
13.同步机制
信号量同步实例
互斥量实例
事件集实现线程同步
14.IO设备模型及实例
15.中断接收方式分析及串口中断接收
16.应用层串口编程实例
17.串口DMA接收数据
18.ADC访问接口实例
19.I2C时序、协议、总线接口
20.线程间通信--邮箱/队列实例
21.队列来实现线程间的通信
c语言笔试题
1.rtthread内核框架与线程调度介绍
有内核库和实时内核(对象管理,实时调度器,线程管理,线程间通信,内存管理,时钟管理,设备管理)libcpu芯片移植,硬件(CPU/RAM/Flash/UART/EMMC等)
内存最小的资源占用情况是3kb rom 1.2kb ram
2.rtthread内核功能启动流程及汇编阶段
启动文件--——rtthread_startup()启动函数【——各种初始化(有系统有关的硬件,定时器,调度器,信号,创建main线程对各个模块依次初始化,启动调度器scheduler,根据规则选择就绪对列优先级最高的线程开始运行,启动定时器线程和空闲线程)】——main()
3.rtthread内存分布
rom({程序占用的flash空间}code代码段、 RO-data只读数据段【存放程序中定义的常量,像const类型】、RW-data读写数据段【存放初始化为非0的全局变量】 ZI-data【0数据段,存放未初始化的全局变量及初始化为0的变量】)
ram
keil文件编译完会生成.map文件——说明各个函数占用的尺寸和地址
烧写可执行程序到stm32后,内存分布会发生变化,stm32上电后默认从flash处启动,启动后把rom中的RW-data搬到RAM中,cpu执行代码从flash中读取,编译器根据给出的ZI地址和大小分配出ZI字段,并将RAM区域清0,这块会变为动态内存堆
4.内核对象管理系统
rtt内核对象有:线程,信号量,互斥量,事件,邮箱,消息队列,定时器,内存池,设备驱动等
对象容器收集了每类内核对象的信息【对象类型,大小等】,对象容器会给每类内核对象分配一个来链表,所有内核对象都会被链接到该链表上。
总结:rtt采用内核对象管理系统来访问/管理所有的内核对象
5.内核配置和剪裁
rtt:高度可裁剪性,在rtconfig.h中,打开/关闭宏定义来对条件进行编译,达到对内核进行精细调整,对组件进行灵活拆卸,达到系统配置和裁剪的目的
常见宏定义
RT_USED RT_UNUSED RT_WEAK【用于连接函数,编译器在链接函数时会优先链接没有该关键字前缀的函数】
ALIGN【作用是给对象分配地址空间时,将其存放的地址按照n字节对齐,n可取2的幂次方】
字节对齐:便于cpu快速访问,合理利用有效节省存储空间
6.线程5种状态
初始——就绪——运行——挂起——关闭
空闲线程:优先级最低,永远为就绪状态,不被挂起,作用【回收被删除的线程资源{僵尸线程}】
线程优先级相同,时间片轮转调度,单位一个时钟节拍
rt_thread_yield():当前线程被换出,相同优先级的下一个就绪线程将被执行。
rt_schedule():当前线程并不一定被换出,而是在系统中选取就绪的优先级最高的线程执行。
创建线程(静态)-----占用RAM空间(RW/ZI 空间),用户分配栈空间和线程句柄:
好处:运行时不需要动态分配内存,运行时效率较高,实时性较好,但内存不能被释放
参考:RTT内核介绍
rthread知识点总结
RT-Thread— 知识点总结(RTT认证+面试题汇总)
7.进程与线程
b,KB,MB
硬盘——抽屉 程序——菜谱 一个可执行程序就是进程
多个程序在内存是进程,进程里面有线程1、线程2 ....
线程是并行的最小单位
8.CPU的内部结构
ARM
控制单元(指令寄存器、指令译码器、操作控制器)
运算单元(算术逻辑单元)
存储单元(专用寄存器与通用寄存器)
时钟
9.中断的优缺点
优点:实现CPU与I/O设备的并行,提高CPU的利用率和系统的性能
缺点:中断处理过程需要保护现场,恢复现场,整个过程需要一定的时间和空间开销,如果中断频率太高,会降低系统的性能
SRAM:静态的随机存储器 【加电下不需要刷新,数据不会丢失,CPU的缓存就是SRAM】
DRAM:动态的随机存储器 【加电的情况下需要不断刷新才能保存数据,最常见的系统内存】
SDRAM:同步动态随机存储器【数据的存储需要时钟来同步,也可用作内存】
10.GPIO的输入输出模式
输入:浮空输入、带上拉输入、带下拉输入、模拟输入
输出:开露输出、推挽输出、开漏复用输出、推挽复用输出
UART、SPI是半双工类型
11.tcp为什么需要3次握手?第3次握手去掉可以不?
第三次握手是为了防止已经失效的连接请求报文突然传输到了服务器,从而建立错误的连接,浪费资源还能防止发生死锁。若服务器发出第二次握手而客户端没有收到,服务器开始传输数据此时客户端便不会理会,导致服务器以为丢包而源源不断地发送数据包,造成死锁。
线程辅助函数 调度器钩子函数
12.系统定时器初始化简介
rtthread定时器:单次触发定时器 周期触发定时器
根据超时函数所处的上下文环境(超时时间到,cpu运行),定时器又可以分为HARD_TIMER模式【默认:在中断的上下文中的执行方式决定了定时器的超时函数不应该调用任何会让当前上下文挂起的系统函数,也不能执行非常长的时间,否则会导致其它中断的响应时间加长或抢占了其它线程执行的时间】与SOFT_TIMER模式
定时器工作机制
当前系统经过tick时间rt_tick(当硬件定时器中断来临时,它将+1),系统创建并激活定时器按照以超时时间排序的方式插入到rt_timer_list链表中(定时器链表rt_timer_list)
13.同步机制
多个执行单元(线程,中断)同时执行临界区,操作临界资源,会导致竞态问题,rtthread提供了3种同步机制
信号量同步实例
//信号量 LED闪烁--信号量控制 信号量的一个应用
#define LED_PIN GET_PIN(F,9)
static rt_sem_t led_sem = RT_NULL;
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static void sem_entry(void *parameter)
{
while(1)
{
rt_sem_release(led_sem);
rt_thread_mdelay(500);
}
}
static void led_entry(void *parameter)
{
static unsigned char cnt = 0;
while(1)
{
if(rt_sem_take(led_sem,RT_WAITING_FOREVER)==RT_EOK)
{
if(cnt++ %2)
{
rt_pin_write(LED_PIN,PIN_HIGH);
}else{
rt_pin_write(LED_PIN,PIN_LOW);
}
rt_thread_mdelay(1000);
rt_sem_release(led_sem);
}
}
}
int led_sample(void)
{//1表示初始计数值,1表示该信号量可用,当被用时会-1,为0,表示不可用,需等待释放为1后才能使用
led_sem=rt_sem_create("led_sem",1,RT_IPC_FLAG_FIFO);
if(led_sem == RT_NULL)
{
rt_kprintf("create led sem failed!\n");
return -RT_ERROR;
}
//512线程zai空间,存储线程的局部变量,函数调用信息和其它运行时的数据
//20线程的优先级,较高的优先值表示较高的优先级
//0线程的标志位,指定线程的属性和行为,0表示没有设置任何特殊的标志位
tid1 = rt_thread_create("ctl_sem",sem_entry,RT_NULL,512,20,0);
if(tid1 != RT_NULL)
{
rt_thread_startup(tid1);
}
tid2 = rt_thread_create("ctl_sem",led_entry,RT_NULL,512,25,0);
if(tid2 != RT_NULL)
{
rt_thread_startup(tid2);
}
}
INIT_APP_EXPORT(led_sample,led dample)
互斥量实例
//互斥量实例
#include <rtthread.h>
/* 定义互斥量 */
static rt_mutex_t mutex;
/* 线程 1 */
static void thread1_entry(void* parameter)
{
/* 获取互斥量 */
rt_mutex_take(mutex, RT_WAITING_FOREVER);
/* 在临界区执行相关操作 */
rt_kprintf("Thread 1 is in critical section\n");
/* 释放互斥量 */
rt_mutex_release(mutex);
}
/* 线程 2 */
static void thread2_entry(void* parameter)
{
/* 获取互斥量 */
rt_mutex_take(mutex, RT_WAITING_FOREVER);
/* 在临界区执行相关操作 */
rt_kprintf("Thread 2 is in critical section\n");
/* 释放互斥量 */
rt_mutex_release(mutex);
}
int main(void)
{
/* 创建互斥量 */
mutex = rt_mutex_create("my_mutex", RT_IPC_FLAG_FIFO);
/* 创建线程 1 */
rt_thread_t thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, 10, 5);
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
/* 创建线程 2 */
rt_thread_t thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, 10, 5);
if (thread2 != RT_NULL)
{
rt_thread_startup(thread2);
}
/* 启动 RT-Thread 调度器 */
rt_thread_mdelay(100);
rt_thread_scheduler_start();
return 0;
}
事件集实现线程同步
//线程间通信--事件
#include <rtthread.h>
#define EVENT_FLAG_A (1<<0) //1左移0位,既不进行位移操作,得到值1
#define EVENT_FLAG_B (1<<1) //1左移1位,得到2,可用于表示不同的事件标志
static rt_event_t event;
void thread1_entry(void *parameter)
{
//等待事件标志A 表示等待事件标志A触发 设置等待行为的标志,接收后清除已触发的
//事件标志,RT_NULL表示接收事件标志指针,表示不需要接收触发的事件标志
rt_event_recv(&event,EVENT_FLAG_A,RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER,RT_NULL);
//执行相应的操作
rt_kprintf("thread1 : event A received.\n");
rt_thread_mdelay(1000);
}
void thread2_entry(void *parameter)
{
rt_event_recv(&event,EVENT_FLAG_B,RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER,RT_NULL);
rt_kprintf("thread2 : Event B received.\n");
rt_thread_mdelay(1000);
}
void event_example(void)
{
//创建事件
event = rt_thread_create("event",RT_IPC_FLAG_FIFO);
if(event==RT_NULL)
{
rt_kprintf("failed to create.\n");
return;
}
//创建线程1
rt_thread_t thread1=rt_thread_create("thread1",thread1_entry,RT_NULL,1024,10,10);
if(thread1 !=RT_NULL)
{
rt_thread_startup(thread1);
}
rt_thread_t thread2=rt_thread_create("thread2",thread2_entry,RT_NULL,1024,20,10);
if(thread2 !=RT_NULL)
{
rt_thread_startup(thread2);
}
while(1)
{
rt_event_send(&event,EVENT_FLAG_A);
rt_thread_mdelay(2000);
rt_event_send(&event,EVENT_FLAG_B);
rt_thread_mdelay(2000);
}
}
参考文章:
【玩转RT-Thread】线程间同步(一) 信号量
RT-Thread 内核学习
14.IO设备模型及实例
#include <rtthread.h>
#include <rtdevice.h>
int main(void)
{
rt_device_t dev;
/* 查找设备 */
dev = rt_device_find("uart1");
if (dev == RT_NULL)
{
rt_kprintf("Device not found!\n");
return -1;
}
/* 打开设备 */
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("Failed to open device!\n");
return -1;
}
/* 向设备写入数据 */
char buffer[] = "Hello, RT-Thread!";
rt_device_write(dev, 0, buffer, sizeof(buffer));
/* 从设备读取数据 */
char read_buffer[32];
rt_device_read(dev, 0, read_buffer, sizeof(read_buffer));
/* 关闭设备 */
rt_device_close(dev);
return 0;
}
15.中断接收方式分析及串口中断接收
#include <rtthread.h>
#include <rtdevice.h>
#define UART_NAME "uart1"
#define BUFFER_SIZE 64
static char rx_buffer[BUFFER_SIZE];
static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
{
/* 从串口接收数据到缓冲区 */
rt_device_read(dev, 0, rx_buffer, size);
/* 处理接收到的数据,例如打印到控制台 */
rt_kprintf("Received data: %s\n", rx_buffer);
return RT_EOK;
}
int main(void)
{
rt_device_t dev;
/* 查找串口设备 */
dev = rt_device_find(UART_NAME);
if (dev == RT_NULL)
{
rt_kprintf("UART device not found!\n");
return -1;
}
/* 打开串口设备 */
if (rt_device_open(dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
{
rt_kprintf("Failed to open UART device!\n");
return -1;
}
/* 注册串口中断回调函数 */
rt_device_set_rx_indicate(dev, uart_rx_ind);
/* 进入主循环 */
while (1)
{
/* 在主循环中处理其他任务 */
rt_thread_mdelay(1000);
}
return 0;
}
16.应用层串口编程实例
#include <rtthread.h>
#include <rtdevice.h>
#define UART_NAME "uart1"
#define BUFFER_SIZE 64
static char tx_buffer[BUFFER_SIZE];
static char rx_buffer[BUFFER_SIZE];
static void uart_thread_entry(void *parameter)
{
rt_device_t dev;
rt_err_t result;
/* 查找串口设备 */
dev = rt_device_find(UART_NAME);
if (dev == RT_NULL)
{
rt_kprintf("UART device not found!\n");
return;
}
/* 打开串口设备 */
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("Failed to open UART device!\n");
return;
}
while (1)
{
/* 向串口发送数据 */
rt_snprintf(tx_buffer, sizeof(tx_buffer), "Hello, RT-Thread!\n");
result = rt_device_write(dev, 0, tx_buffer, rt_strlen(tx_buffer));
if (result != rt_strlen(tx_buffer))
{
rt_kprintf("Failed to send data!\n");
}
/* 从串口接收数据 */
result = rt_device_read(dev, 0, rx_buffer, sizeof(rx_buffer) - 1);
if (result > 0)
{
rx_buffer[result] = '\0';
rt_kprintf("Received data: %s\n", rx_buffer);
}
/* 延时一段时间 */
rt_thread_mdelay(1000);
}
}
int main(void)
{
rt_thread_t thread;
/* 创建串口线程 */
thread = rt_thread_create("uart", uart_thread_entry, RT_NULL, 1024, 10, 10);
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
rt_kprintf("Failed to create uart thread!\n");
}
/* 进入主循环 */
while (1)
{
/* 在主循环中处理其他任务 */
rt_thread_mdelay(1000);
}
return 0;
}
17.串口DMA接收数据
#include <rtthread.h>
#include <rtdevice.h>
#define UART_NAME "uart1"
#define BUFFER_SIZE 64
static char rx_buffer[BUFFER_SIZE];
static void uart_dma_rx_callback(rt_device_t dev, rt_size_t size)
{
/* 处理接收到的数据,例如打印到控制台 */
rt_kprintf("Received data: %s\n", rx_buffer);
}
int main(void)
{
rt_device_t dev;
rt_err_t result;
/* 查找串口设备 */
dev = rt_device_find(UART_NAME);
if (dev == RT_NULL)
{
rt_kprintf("UART device not found!\n");
return -1;
}
/* 打开串口设备 */
if (rt_device_open(dev, RT_DEVICE_FLAG_DMA_RX) != RT_EOK)
{
rt_kprintf("Failed to open UART device!\n");
return -1;
}
/* 配置串口 DMA 接收参数 */
result = rt_device_control(dev, RT_DEVICE_CTRL_UART_SET_RX_DMA, RT_NULL);
if (result != RT_EOK)
{
rt_kprintf("Failed to configure UART DMA!\n");
return -1;
}
/* 注册串口 DMA 接收回调函数 */
rt_device_set_rx_complete(dev, uart_dma_rx_callback);
/* 启动串口 DMA 接收 */
result = rt_device_control(dev, RT_DEVICE_CTRL_UART_RX_DMA_START, RT_NULL);
if (result != RT_EOK)
{
rt_kprintf("Failed to start UART DMA receive!\n");
return -1;
}
/* 进入主循环 */
while (1)
{
/* 在主循环中处理其他任务 */
rt_thread_mdelay(1000);
}
return 0;
}
18.ADC访问接口实例
#include <rtthread.h>
#include <rtdevice.h>
#define ADC_NAME "adc1"
int main(void)
{
rt_device_t dev;
rt_err_t result;
rt_uint32_t adc_value;
/* 查找 ADC 设备 */
dev = rt_device_find(ADC_NAME);
if (dev == RT_NULL)
{
rt_kprintf("ADC device not found!\n");
return -1;
}
/* 打开 ADC 设备 */
if (rt_device_open(dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
{
rt_kprintf("Failed to open ADC device!\n");
return -1;
}
/* 读取 ADC 值 */
result = rt_device_read(dev, 0, &adc_value, sizeof(adc_value));
if (result != sizeof(adc_value))
{
rt_kprintf("Failed to read ADC value!\n");
return -1;
}
/* 打印 ADC 值 */
rt_kprintf("ADC value: %u\n", adc_value);
/* 关闭 ADC 设备 */
rt_device_close(dev);
return 0;
}
19.I2C时序、协议、总线接口
20.线程间通信--邮箱/队列实例
//邮箱
#define MAILBOX_SIZE 5
static rt_mailbox_t mailbox;
void sender_thread_entry(void *parameter)
{
rt_uint32_t count = 0;
while(1)
{
//创建消息并发送到邮箱中:邮箱句柄,发送数据的地址,发送数据的大小
if(rt_mb_send(mailbox,&count,sizeof(count))==RT_EOK)
{
rt_kprintf("sender:sent data %d\n",count);
count++;
}
rt_thread_mdelay(1000);
}
}
void receiver_thread_entry(void *parameter)
{
rt_uint32_t data;
while(1)
{
//从邮箱中接收消息 接收数据存放的地址,接收数据的大小
if(rt_mb_recv(mailbox,&data,sizeof(data),RT_WAITING_FOREVER)==RT_EOK)
{
rt_kprintf("receiver:received data %d\n",data);
//处理接收到的消息
}
}
}
int mailbox_example(void)
{
mailbox = rt_mb_create("mailbox",MAILBOX_SIZE,RT_IPC_FLAG_FIFO);
if(mailbox== RT_NULL)
{
rt_kprintf("failed to create mailbox.\n");
return -1;
}
//最后一个10是线程的抢占式时间片,用于确定线程在多任务系统中的调度时间
//线程在执行时被分配的时间片为10个周期,当线程的时间片用尽时,操作系统将中断线程的执行
rt_thread_t sender_thread = rt_thread_create("sender",sender_thread_entry,RT_NULL,1024,10,10);
if(sender_thread != RT_NULL)
{
rt_thread_startup(sender_thread);
}
rt_thread_t receiver_thread = rt_thread_create("receiver",receiver_thread_entry,RT_NULL,1024,20,10);
if(receiver_thread != RT_NULL)
{
rt_thread_startup(receiver_thread);
}
return 0;
}
21.队列来实现线程间的通信
#include <rtthread.h>
#define QUEUE_SIZE 5
static rt_queue_t queue;
static rt_thread_t sender_thread;
static rt_thread_t receiver_thread;
void sender_thread_entry(void *parameter)
{
rt_uint32_t count = 0;
while(1)
{ //创建消息并发送到队列中
if(rt_queue_send(queue,&count,sizeof(count))==RT_EOK)
{
rt_kprintf("Sender:send data %d \n",count);
count++;
}
rt_thread_mdelay(1000);
}
}
void receiver_thread_entry(void *parameter)
{
rt_uint32_t data;
while(1)
{
if(rt_queue_recv(queue,&data,sizeof(data),RT_WAIYING_FOREVER)==RT_EOK)
{
rt_kprintf("Receiver:receiver data %d\n",data);
//处理接收到的数据消息
}
}
}
int queue_example(void)
{ //队列的大小,队列中每个元素的大小
queue = rt_queue_create("queue",QUEUE_SIZE,sizeof(rt_uint32_t),RT_IPC_FLAG_FIFO);
if(queue == RT_NULL)
{
rt_kprintf("Failed to create queue.\n");
return -1;
}
//创建发送线程
sender_thread = rt_thread_create("sender",sender_thread_entry,RT_NULL,1024,10,10);
if(sender_thread!=RT_NULL)
{
rt_thread_startup(sender_thread);
}
//创建接收线程
receiver_thread = rt_thread_create("receiver",receiver_thread_entry,RT_NULL,1024,10,10);
if(receiver_thread!=RT_NULL)
{
rt_thread_startup(receiver_thread);
}
return 0;
}
c语言笔试题
1.对于嵌入式系统来说,直接操作硬件寄存器代码,通常使用易失性(volatile)关键字
来声明寄存器地址映射的变量(对)
2.使用递归反向输出字符串的例子
//hello olleh
#include "stdio.h"
#include "string.h"
char *reverse(char *str)
{
if( !str ) //判断输入字符串是否为空串
{
return NULL; //若为空串则返回NULL
}
int len = strlen(str);
if( len > 1 )
{
char ctemp =str[0];
str[0] = str[len-1];
str[len-1] = '\0'; // 最后一个字符在下次递归时不再处理
reverse(str+1); // 递归调用
str[len-1] = ctemp;
}
return str;
}
int main()
{
char str[100];
gets(str);
reverse(str);
puts(str);
return 0;
}
3.(void *)ptr 和(*(void**))ptr
(void *)ptr 接受任何类型的指针变量,它仅仅接收的是变量的首地址
指针的类型是指明指针向后偏移多少个字节
*(void**)先解引用void* ---(void *)ptr 所以它两表示的一样
4.在c语言中,结构体的成员是按照它们声明的顺序存储在内存中,内存对齐会导致结构体的成员在内存中的偏移量可能会增加(对)
5.
unsigned int a=6;
int b=-20;
a+b>6? 对的,-20 ----0xffffffec 默认会把有符合的数当成无符号的数计数,显然>6
6.单链表的建立,把'a'--'z'26个字母倒序插入到链表中,并且输出打印值
初始化一个链表,改变链表的指向,让z指向上一个
打印链表的值函数
typedef struct linklist
{
char ff;
struct linklist *next;
}Node;
typedef Node* Linklist;
int main()
{
Listlist L,new;
L = (Linklist)malloc(sizeof(Node));
L->next =NULL;
int i;
for(i =0;i<26;i++) //L->next赋值给new->next,
new->ff ='a'+i; //
new->next L->next;
L->next =new;
}
while(L->next)
{
printf("%c ",L->next->ff);
L =L->next;
}
printf("\n");
return 0;
}
7.不用库函数,用c语言实现将一整型数字转化为字符串
123 -->"123" -123 -->"-123"
int a flag;
scanf("%d ",&a);
char str[100] = {0};
写一个函数实现该功能:
char inttostr(int num,char str)
{
if(num<0) {num = -num;s[i++]='-';}
if(num == 0) s[i++]='0';
while(num>0)
{
str[i++]=num%10+'0';
n=n/10;
}
//反转字符串
int start=0;int end =i-1;
while(start<end)
{
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end --;
}
str[i]='\0' ;
}
int main(){
}
8.rtthread中环形缓冲区实例
//环形缓冲区
#include <rtthread.h>
#define BUFFER_SIZE 16
static rt_ringbuffer_t ringbuffer;
static char buffer[BUFFER_SIZE];
void producer_thread_entry(void *parameter)
{
int count = 0;
while (1)
{
/* 将数据写入环形缓冲区 */
rt_ringbuffer_put(&ringbuffer, &count, sizeof(int));
rt_kprintf("Producer: Write data %d\n", count);
count++;
/* 延时一段时间 */
rt_thread_mdelay(1000);
}
}
void consumer_thread_entry(void *parameter)
{
int data;
while (1)
{
/* 从环形缓冲区读取数据 */
rt_ringbuffer_get(&ringbuffer, &data, sizeof(int));
rt_kprintf("Consumer: Read data %d\n", data);
/* 延时一段时间 */
rt_thread_mdelay(2000);
}
}
void ringbuffer_example(void)
{
/* 创建环形缓冲区 */
rt_ringbuffer_init(&ringbuffer, buffer, BUFFER_SIZE);
/* 创建生产者线程 */
rt_thread_t producer_thread = rt_thread_create("producer", producer_thread_entry, RT_NULL, 1024, 10, 10);
if (producer_thread != RT_NULL)
{
rt_thread_startup(producer_thread);
}
/* 创建消费者线程 */
rt_thread_t consumer_thread = rt_thread_create("consumer", consumer_thread_entry, RT_NULL, 1024, 20, 10);
if (consumer_thread != RT_NULL)
{
rt_thread_startup(consumer_thread);
}
}
9.rtthtread中定时器实例
#include <rtthread.h>
static rt_timer_t timer;
static void timeout(void *parameter)
{
rt_kprintf("One-shot timer is timeout.\n");
}
int timer_example(void)
{
timer = rt_timer_create("timer",timeout,RT_NULL,30,RT_TIMER_FLAG_ONE_SHOT|PERIODIC
//periodic
if(timer != RT_NULL)
{
rt_timer_start(timer);
}
else{
LOG_E("rt_timer_create failed.\n");
return -RT_ERROR;
}
return 0;
}
10.
scanf("%d",&num1) 输入语句
常量有哪些?
字面常量(100) const修饰的常变量(const float pai =3.14f;)
#define定义的标识符常量
枚举常量(enum 默认从0开始)
\\转义字符 ,用于表示一个反斜杠\,防止被解析为一个转义序列符
printf("%c\n",'\'');
printf("%s\n","\"");
c语言的死循环:
for(;;) while(1)
11.c语言字符类型
char short int long
long long
float double
12.单链表的建立,把'a'--'z'26个字母倒序插入到链表中,并且输出打印值
初始化一个链表,改变链表的指向,让z指向上一个
打印链表的值函数
写法1:
typedef struct linklist
{
char ff;
struct linklist *next;
}Node;
typedef Node* Linklist;
int main()
{
Listlist L,new;
L = (Linklist)malloc(sizeof(Node));
L->next =NULL;
int i;
for(i =0;i<26;i++) //L->next赋值给new->next,
new->ff ='a'+i; //
new->next L->next;
L->next =new;
}
while(L->next)
{
printf("%c ",L->next->ff);
L =L->next;
}
printf("\n");
return 0;
}
写法2:
#include <stdio.h>
#include <stdlib.h>
struct Node{
char data;
struct Node* next;
}
void insertNode(struct Node** head,char data)
{
struct Node* newNode =(struct Node*)mallac(sizeof(struct Node));
newNode->data =data;
newNode->next =*head; //将新节点的指针指向当前链表的头节点,将新节点指向原本的头节点,使新节点成为新的头节点
*head =newNode; //将链表的头指针指向新节点,这样可以修改指针head所指向的值,将其更新为新节点的地址
}
void printList(struct Node* head){
struct Node* curr=head;
while(curr!=NULL){
printf("%c ",curr->data);
curr = curr->next;
}
printf("\n");
}
int main(){
struct Node* head =NULL;
//插入‘a’到‘z’的26个字母倒序
for(char c = 'z';c>='a';c--)
{
insertNode(&head,c);
}
printList(head);
}
13.
大端存储:数据的地位保存在内存的高地址中
全局变量在静态存储区,局部变量在堆载中
悬空指针:没有指向正确内存位置
当删除或释放对象时,如果不修改指针的值或置为NULL,就会出现悬空指针
野指针:只声明没有初始化的指针,它可以指向任何内存
int *const p 指针常量,指针在前,常量在后,p只能指向一个位置,而不能指向其它位置
,指像的变量值可以改变
参考文章:rtt时钟管理
时间复杂度和空间复杂度
C语言链表超详解