树莓派笔记--内核编译


硬件平台: 树莓派4B
内核版本: linux-rpi-4.19


下载地址:
https://github.com/raspberrypi
linux - 内核源代码
tools - 编译需要的工具
firmware - 官方编译好的固件(不需要)

不建议用git命令下载,太慢了,可以用工具下载zip格式,另外,如果从kernel.org官网下载,对于树莓派或多或少的都会有一些小的问题,所以树莓派会在github上对版本进行修改,建议直接在github上下载更新。也可以通过百度网盘下载,提取码:pj8x


编译器说明:

tools/bcm2708目录下一共有5个编译器:

# ls
arm-bcm2708hardfp-linux-gnueabi  
arm-rpi-4.9.3-linux-gnueabihf
arm-bcm2708-linux-gnueabi        
gcc-linaro-arm-linux-gnueabihf-raspbian
arm-linux-gnueabihf              
gcc-linaro-arm-linux-gnueabihf-raspbian-x64

其中x64是64位编译器,arm-bcm2708-linux-gnueabi是指向arm-rpi-4.9.3-linux-gnueabihf的链接,其他有应该是版本不同,都可以用。将编译器路径加入PATH(arm-linux-gnueabihf)。


1. 解压内核源码,生成.config配置文件:

# KERNEL=kernel7l
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711_defconfig

2. 编译:内核/模块/设备树

# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs

3.安装内核:
将arch/arm/boot/zImage复制至TF卡boot分区,替换掉原来的kernel7l.img文件即可。

# cp arch/arm/boot/zImage /media/user/boot/${KERNEL}.img

或者修改boot目录下config.txt文件指定

kernel = zImage

注意事项:
这里有个大坑,很多文档说要用工具解压转换一下zImage,实际上不用,转换了反而还不能正常启动了。


4.安装模块:

# make modules_install INSTALL_MOD_PATH=/media/user/rootfs/

5.安装设备树:

# cp arch/arm/boot/dts/bcm2711-rpi-4-b.dtb $/media/user/boot/

- 阅读全文 -

Linux应用程--BMP与JPG格式图片解码


文章摘要: 本文主要描述了BMP/JPG解码及显示的示例。
操作系统: Debian


BMP数据结构:

// BMP文件头(54字节),其中文件头14字节,图像信息40字节
#pragma pack(1) //设置内存按照1个字节对齐
typedef struct
{
    char bfType[2];           // 00 文件类型: 固定为BM
    uint32_t bfSize;          // 02 文件大小:单位字节
    uint16_t bfReserved1;     // 06 保留字段
    uint16_t bfReserved2;     // 08 保留字段
    uint32_t bfOffBits;       // 10 数据偏移
    uint32_t biSize;          // 14 信息结构长度
    uint32_t biWidth;         // 18 图像宽度
    uint32_t biHeight;        // 22 图像高度
    uint16_t biPlanes;        // 26 颜色面板数(总是为1)
    uint16_t biBitCount;      // 28 像素位数
    uint32_t biCompression;   // 30 压缩格式
    uint32_t biSizeImage;     // 34 图像内容大小
    uint32_t biXPelsPerMeter; // 38 水平分辨率 像素/米
    uint32_t biYPelsPerMeter; // 42 竖直分辨率 像素/米
    uint32_t biClrUsed;       // 46 调色板数量
    uint32_t biClrImportant;  // 50 关键色彩数
} BITMAPINFOHEADER, bmp_info_t;
#pragma pack()

BMP文件解析:

bmp_info_t* bmp_info = (void*)&buff[0];
// 正常情况是读出54个字节
fread(buff, 54, 1, fp);
dump_data(buff, 54);
printf("文件类型: %c%c\r\n",     bmp_info->bfType[0], bmp_info->bfType[1]);
printf("文件大小: %d 字节\r\n",  bmp_info->bfSize);
printf("数据偏移: %d\r\n",       bmp_info->bfOffBits);
printf("信息长度: %d\r\n",       bmp_info->biSize);
printf("图像尺寸: %dx%d\r\n",    bmp_info->biWidth, bmp_info->biHeight);
printf("像素位数: %d bits\r\n",  bmp_info->biBitCount);
printf("压缩格式: %d\r\n",       bmp_info->biCompression);
printf("图像内容: %d 字节\r\n",  bmp_info->biSizeImage);    
printf("分 辨 率: %d,%d\r\n",    bmp_info->biXPelsPerMeter, bmp_info->biXPelsPerMeter);
printf("调色板数: %d\r\n\r\n",   bmp_info->biClrUsed);
// 如果文件头大于54个字节,需要读出其他数据
if(bmp_info->bfOffBits > 54)
{
    fread(buff, bmp_info->bfOffBits - 54, 1, fp);
}
// 申请读取一行数据需要的内存    
int line_size = bmp_info->biWidth * bmp_info->biBitCount / 8;

// BMP文件和每行长度必须以4字节对齐,不足部分填充0,所以必须全部读出来  
// 否则显示会错位 
line_size  = (line_size + 3 )/ 4 * 4;    
     
char *pline = malloc(line_size);
if (pline == NULL)
{
    perror("内存分配失败");
    exit(-2);
}  
//----------------------------
int xcnt;
int ycnt;
int offset; // 偏移
char *p2;
char *p3;

// 屏幕和图像尺寸取最小值
xcnt = (bmp_info->biWidth  < vinfo.xres)?bmp_info->biWidth :vinfo.xres;
ycnt = (bmp_info->biHeight < vinfo.yres)?bmp_info->biHeight:vinfo.yres;
while (ycnt--)
{    
    // 从文件中读一行数据出来    
    fread((char *)pline, 1, line_size, fp);     
    // 计算偏移值
    offset = (ycnt) * finfo.line_length; 
    p2 = (char *)(pfb + offset);
    p3 = pline;        
    for (j = 0; j < xcnt; j++)
    {
        *p2++ = *p3++;    // 先按24bit显示
        *p2++ = *p3++;
        *p2++ = *p3++;
        *p2++ = 0;
    }
}

JPG图像解码:
需要添加libjpeg-dev支持;
编译选项:-ljpeg

// 指定图片的路径就可以调用这个jpg的解析
// output_width:      图像的输出宽度
// output_height:    图像的输出高度
// output_component: 每个像素的分量数,也即字节数(3或4字节)
int jpg_display(char *path)
{
    // 1. 分配并初始化一个jpeg解压对象
    struct jpeg_decompress_struct dinfo;
    struct jpeg_error_mgr jerr;        // 错误变量
    dinfo.err = jpeg_std_error(&jerr); //
    jpeg_create_decompress(&dinfo);    // 初始化

    // 2. 打开JPG图像文件
    FILE *infile;
    infile = fopen(path, "r");
    if (infile == NULL)
    {
        perror("fopen error");
        return -1;
    }
    jpeg_stdio_src(&dinfo, infile); //为解压对象dinfo指定要解压的文件

    // 3. 获取图像信息
    jpeg_read_header(&dinfo, TRUE);    
    //-------------------------------------------------
    // 4. 设置jpeg解压缩对象dinfo的一些参数,可采用默认参数
    //-------------------------------------------------
    // 5. 解码文件头
    jpeg_start_decompress(&dinfo);
    printf("图像信息: %dx%d %d\r\n", dinfo.output_width, dinfo.output_height, dinfo.output_components);
    //-----------------------------------------------------
    // 6. 读取一行或者多行扫描线数据并处理
    // 分配内存以便读入数据
    unsigned char *line_buff = malloc(dinfo.output_width * dinfo.output_components);
    if(line_buff == NULL)
    {
        perror("malloc failed");
        exit(1);
    }    
    while (dinfo.output_scanline < dinfo.output_height)
    {
       // 读取一行数据并解码(output_scanlinee表示当前行数,会自动增加)
       jpeg_read_scanlines(&dinfo, &line_buff, 1);
       // 此处加入数据后续处理 
       // BMP的顺序为BGR, 显示器顺序为BGRA, JPG的顺序为RGB      
       // 如果直接输出为图片,则显示为上下翻转且颜色也不对(BMP本身的问题)       
    }
    
    // 完成解压过程
    jpeg_finish_decompress(&dinfo);

    // 释放内容
    jpeg_destroy_decompress(&dinfo);
    return 0;
}

调整图像(输出BMP格式文件):

int sp;
int offset;        
unsigned char *file_buff = malloc(line_size * dinfo.output_height);
unsigned char *line_buff = malloc(line_size);
if((file_buff == NULL) || (line_buff == NULL))
{
    exit(1);
}
while (dinfo.output_scanline < dinfo.output_height)
{        
    offset = (dinfo.output_height - dinfo.output_scanline - 1) * line_size;
    unsigned char *p = file_buff + offset;
    jpeg_read_scanlines(&dinfo, &line_buff, 1);
    sp = 0;
    for (int i = 0; i < dinfo.output_width; i++)
    {
        *p++ = line_buff[sp+2];
        *p++ = line_buff[sp+1];
        *p++ = line_buff[sp];
        sp  += dinfo.output_components;
    }        
} 
   
FILE* bmp_fp = bmp_file_init(&dinfo);
fwrite(file_buff,1,dinfo.output_width*dinfo.output_components*dinfo.output_height,bmp_fp);
fclose(bmp_fp);

BMP文件头处理:

FILE* bmp_file_init(struct jpeg_decompress_struct* j)
{
    FILE *fp;
    bmp_info_t bmp_info;
    long int img_size = j->output_width * j->output_height * j->output_components;

    bmp_info.bfType[0] = 'B';
    bmp_info.bfType[1] = 'M';
    bmp_info.bfSize = img_size + 54;      // 文件大小
    bmp_info.bfReserved1 = 0;             // 06 保留
    bmp_info.bfReserved2 = 0;             // 08 保留
    bmp_info.bfOffBits = 54;              // 10 数据偏移(一般都为54)
    bmp_info.biSize = 40;                 // 14 信息结构长度
    bmp_info.biWidth = j->output_width;   // 18 图像宽度
    bmp_info.biHeight = j->output_height; // 22 图像高度
    bmp_info.biPlanes = 1;                // 26 颜色面板数(总是为1)
    bmp_info.biBitCount = 24;             // 28 像素位数
    bmp_info.biCompression = 0;           // 30 压缩格式
    bmp_info.biSizeImage = img_size;      // 34 图像内容大小
    bmp_info.biXPelsPerMeter = 1024;      // 38 水平分辨率 像素/米
    bmp_info.biYPelsPerMeter = 1024;      // 42 竖直分辨率 像素/米
    bmp_info.biClrUsed = 0;               // 46 调色板数量
    bmp_info.biClrImportant = 0;          // 50 关键色彩数

    fp = fopen("output.bmp", "wb");
    if(fp == NULL)
    {
        perror("Open Bmp File Failed");
        exit(-1);
    }
    fwrite(&bmp_info,1,54,fp);
    
    return fp;
}   

- 阅读全文 -

Linux应用程序--FrameBuffer帧缓冲应用


文章说明: Linux显示基于FrameBuffer帧缓冲
硬件平台: 树莓派4B
测试说明: 由于桌面系统会一直刷新屏幕,需要关闭桌面,进入单用户模式测试(init 3);


打开设备文件:

#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

int fb;
fb = open("/dev/fb0", O_RDWR);
if (fd < 0)
{
    perror("can not open device");
    exit(1);
}
// 获取显示器可变参数
if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo))
{
  perror("get screen var info");
  exit(2);
}
printf("屏幕分辨率: %dx%d %dbit\r\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

// 获取显示器固定参数
if (ioctl(fb, FBIOGET_FSCREENINFO, &finfo))
{
  perror("get screen fix info");
  exit(3);
}
// 每行字节数 = 行像素数*每个像素的字节数
printf("每行字节数: %d\r\n", finfo.line_length);

将FrameBuff映射至内存区:

int mem_size = vinfo.xres*vinfo.yres*vinfo.bits_per_pixel/8;
// 内存映射
pfb = (char*)mmap(0, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
if(pfb == NULL)
{
  perror("mmap error");
}
int x, y;
int offset;
// 画一个300*200的蓝色矩形
for (y = 0; y < 200; y++)
{
  offset = y * finfo.line_length;
  for (x = 0; x < 300; x++)
  {
    // 计算内存位置(bpp=32bit)
    *(pfb + offset)     = 255; // B
    *(pfb + offset + 1) = 0;   // G
    *(pfb + offset + 2) = 0;   // R
    *(pfb + offset + 3) = 0;   // 透明度(没发现啥用)
    offset += 4;               // 下一个位置(vinfo.bits_per_pixel / 8)
  }
}

从文件读取24位图BMP文件并显示:
由于24位图文件仅存了RGB信息,与fb的32bit的不匹配,所以不能直接用cat命令显示,设置第4个字节的信息。

int fd;
int ret;
char buff[100];
char* p = pfb;

fd = open("./Tulips.bmp", O_RDONLY);
if(fd < 1)
{
  perror("can not open file");
}
// 跳过54字节的BMP文件头
ret= read(fd, buff, 54);         

while(cnt)
{
  ret = read(fd, buff, 3);
  *p++ = buff[0];     // 将前3个字节的RGB信息原样输出
  *p++ = buff[1];
  *p++ = buff[2];
  *p++ = 0;           // 设置第4字节的信息
}
close(fd);

显示的图倒立的,原因是BMP格式本身的问题,需要进行显示调整:

int *p = (int *)(pfb + mem_size) - 1;
while (read(fd, (char *)p--, 3))
{
  if (p < pfb)
  {
    break;
  }
}

- 阅读全文 -

Linux系统应用--路由表配置


文章摘要: 本文主要描述了Linux系统下路由表的配置详细说明

命令格式:

# route  [add|del] [-net|-host] target [netmask Nm] [gw Gw] [[dev] If]

add : 添加一条路由规则
del : 删除一条路由规则

-net : 目的地址是一个网络(后边跟网段/掩码)
-host : 目的地址是一个主机(后边跟单个主机地址)

target : 目的网络或主机
netmask : 目的地址的网络掩码
gw : 路由数据包通过的网关
dev : 为路由指定的网络接口


查看当前路由表:

# route
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.10.1    0.0.0.0         UG    1024   0        0 eth0
192.168.10.0    *               255.255.254.0   U     0      0        0 eth0

Destination: 目标地址,default表示未明确定义的所有地址;
Gateway: 网关地址,*表示与本机相同的网段不需要指定网关;
Genmask:子网掩码
Iface: 网络接口


添加默认网关:

# route add default gw 192.168.10.1

执行完成后路由表如下:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.10.1    0.0.0.0         UG    1024   0        0 eth0

添加一条路由: 该网段的数据从指定网关通过

# route add -net 6.6.6.0/24 gw 192.168.10.1

删除默认路由:

# route del default 

删除指定路由:

# route del –net 6.6.6.0/24

- 阅读全文 -

Mysql数据库应用脚本


创建数据库用户

#!/bin/sh

mysql -u root -p <<END
USE mysql;
CREATE USER 'user';
SET PASSWORD FOR 'user'=PASSWORD('123456');
\q
END

创建数据库并授权用户:

#!/bin/sh
  
mysql -u root -p <<END
CREATE DATABASE $1;
GRANT ALL PRIVILEGES ON  $1.* to $2@localhost;
FLUSH PRIVILEGES
\q
END

- 阅读全文 -


Copyright©2025 春天花会开, All Rights Reserved. Email: webmaster@oroct.com