Linux嵌入式方向的常见面试题
libudev是udev是一种开源实现库,它能根据系统中硬件设备的状态来动态的更新设备文件,包括设备文件的创建、删除等。使用udev后,在/dev目录下就直包含系统中真正存在的设备。udev同时提供了建设接口,当设备的状态发生改变时,监视接口可以发送对应的事件给应用程序。主要管理/dev目录下的设备节点,同时,也接替devfs、hotplug热插拔的功能,处理添加硬件、删除硬件、加载firmwar
1、简述Linux内核的启动流程
Linux内核的启动流程,包含引导过程、内核初始化这2个步骤。
- 1)引导过程:系统上电,CPU自身初始化;然后是BIOS加电自检,加载内核引导程序,内核引导程序加载已经压缩的内核,再对压缩内核进行解压;
- 2)内核初始化:调用start_kernel()完成大部分的初始化工作,调用reset_init()启动内核线程,调用kernel_init()完成设备驱动程序的初始化,调用init_post()来启动用户空间的init进程。
2、libudev的使用场景
libudev是一种函数库,专门用来在Linux上做设备管理的。主要管理/dev目录下的设备节点,同时,也接替devfs、hotplug热插拔的功能,处理添加硬件、删除硬件、加载firmware,以及相关用户空间的行为。
libudev是udev是一种开源实现库,它能根据系统中硬件设备的状态来动态的更新设备文件,包括设备文件的创建、删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就直包含系统中真正存在的设备。udev同时提供了监视接口,当设备的状态发生改变时,监视接口可以发送对应的事件给应用程序。比如,设备插上,会发送add事件;设备拔除,会发送remove事件。
3、对一个整数,进行因式分解
使用C语言,对一个整数,进行因式分解,比如,24=2*2*2*3。
int Factor(int num)
{
for (int i = 2; i < num; i++)
{
while( num != i) {
if(num % i == 0) {
printf("%d * ",i);
num = num/i;
}
else {
break;
}
}
}
printf("%d\n",num);
return num;
}
4、不用中间变量交换整数a和b
a = a+b;
b = a-b;
a = a-b;
5、用条件表达式,判断整数num是否为2的整数幂
((num & (num-1))==0)?true:false;
6、数字0x6F的第3位设置为1
int num = 0x6F | (0x1<<2);
//扩展,由于索引从0开始,
//将第n位置1,则bit=n-1,范围是[0,n-1]
void SetBit(int& num, int bit)
{
num |= (0x1<<bit);
}
7、数字0x6F的第3位设置为0
int num = 0x6F & (~(0x1<<2));
//扩展
//将第n位置0,bit=n-1, [b7,b6,b5,b4,b3,b2,b1,b0]
void ClsBit(int& num, int bit)
{
num &= ~(0x1<<bit);
}
8、运算符new、delete的重载
在C++中new、delete分别是创建对象、释放对象的运算符。
- new主要做三件事:初始化对象、分配空间、返回指针;
- delete主要是删除对象或者指针,释放内存;
#include <iostream>
using namespace std;
class Object{
public:
//重载new运算符
void* operator new(std::size_t size)
{
return std::malloc(size);
}
//重载delete运算符
void operator delete(void* ptr)
{
std::free(ptr);
}
};
int main(){
Object* obj = new Object;
cout<<sizeof(obj)<<endl;
delete obj;
return 0;
}
9 简单介绍一下I2C
I2C总线是由飞利浦(Philips)公司开发的一种双向二线制同步串行总线,实现有效的IC间的控制,它只需要两根线(SDA和SCL)即可在连接于总线上的器件之间传送信息。
I2C总线在传输数据都是按照bit来传送。SCL为时钟线,SDA为数据线;在SCL时钟线为高电平时,SDA数据线上的电平不允许被修改,SCL时钟线为低电平时,SDA数据线上的电平可为高/低。I2C总线的位传输,如下图所示1。

10 CAN的初始化
CAN(Controller Area Network)全称: 控制器局域网,是一种广泛应用于汽车、工业自动化等领域的串行通信协议。下面分别介绍在不同硬件平台下 CAN 驱动初始化的相关内容。
10.1 在SMT32上(使用HAL库)
a) 配置CubeMX
- 使能 CAN 外设:打开 STM32CubeMX,选择对应的芯片型号,在 “Pinout & Configuration” 中找到 “Connectivity” 下的 “CAN”,使能 CAN1 或 CAN2。
- 配置 GPIO:CAN 的收发引脚会自动配置,通常 CAN_RX 为输入模式,CAN_TX 为复用推挽输出模式。
- 配置 CAN 参数:在 “Parameter Settings” 中配置 CAN 的波特率、工作模式等参数。例如,波特率为 500kbps,可根据 CAN 时钟频率和位时间参数进行计算和设置。
- 生成代码:配置完成后,点击 “Project” -> “Generate Code” 生成 MDK 或 IAR 工程。
b) 编写初始化代码
SMT32平台上,CAN的初始化代码如下:
#include "main.h"
#include "stm32f4xx_hal.h"
CAN_HandleTypeDef hcan1;
void MX_CAN1_Init(void)
{
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 10; // 根据波特率计算得出
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_6TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_3TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = ENABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
Error_Handler();
}
}
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
/* CAN1 clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**CAN1 GPIO Configuration
PA11 ------> CAN1_RX
PA12 ------> CAN1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
c) 配置过滤器
CAN 过滤器用于筛选接收到的报文,只让符合条件的报文进入接收 FIFO。以下是一个简单的过滤器配置示例:
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
10.2 在Linux上(使用SocketCAN)
a)加载 CAN 内核模块
在 Linux 系统中,首先需要加载 CAN 相关的内核模块,例如can、can_raw和mttcan(如果使用的是 MTTCAN 控制器)。可以使用以下命令加载模块:
sudo modprobe can
sudo modprobe can_raw
sudo modprobe mttcan
b) 配置 CAN 接口
使用ip link命令配置 CAN 接口的波特率、工作模式等参数,并启用 CAN 接口。例如,配置 CAN0 接口的波特率为 500kbps:
sudo ip link set can0 type can bitrate 500000
sudo ip link set up can0
c) 编写初始化代码
以下是一个简单的 C 语言示例,用于初始化 CAN 接口并发送一个 CAN 报文:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main()
{
int s;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
// 创建CAN套接字
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("socket");
return 1;
}
// 指定CAN接口
strcpy(ifr.ifr_name, "can0");
ioctl(s, SIOCGIFINDEX, &ifr);
// 绑定套接字到CAN接口
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}
// 配置CAN帧
frame.can_id = 0x123;
frame.can_dlc = 8;
for (int i = 0; i < 8; i++) {
frame.data[i] = i;
}
// 发送CAN帧
if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
perror("write");
return 1;
}
close(s);
return 0;
}
参考文章:
【1】 SMT32嵌入式开发.2024年.详解嵌入式开发中的I2C总线
-
1 ↩︎
更多推荐
所有评论(0)