树莓派控制Hobbywing 电调控制无刷电机

树莓派4B + ESC (Hobbywing) + SKYwalker 2212 无刷电机

测试步骤

  1. 搭建电路
  2. 校准电调
  3. 驱动电机ESC.py 测试代码:
# This program will let you test your ESC and brushless motor.
# Make sure your battery is not connected if you are going to calibrate it at first.
# Since you are testing your motor, I hope you don't have your propeller attached to it otherwise you are in trouble my friend...?
# This program is made by AGT @instructable.com. DO NOT REPUBLISH THIS PROGRAM... actually the program itself is harmful                                             pssst Its not, its safe.

import os     #importing os library so as to communicate with the system
import time   #importing time library to make Rpi wait because its too impatient 
os.system ("sudo pigpiod") #Launching GPIO library
time.sleep(1) # As i said it is too impatient and so if this delay is removed you will get an error
import pigpio #importing GPIO library

ESC=4  #Connect the ESC in this GPIO pin 

pi = pigpio.pi();
pi.set_servo_pulsewidth(ESC, 0) 

max_value = 2000 #change this if your ESC's max value is different or leave it be
min_value = 700  #change this if your ESC's min value is different or leave it be
print "For first time launch, select calibrate"
print "Type the exact word for the function you want"
print "calibrate OR manual OR control OR arm OR stop"

def manual_drive(): #You will use this function to program your ESC if required
    print "You have selected manual option so give a value between 0 and you max value"    
    while True:
        inp = raw_input()
        if inp == "stop":
            stop()
            break
        elif inp == "control":
            control()
            break
        elif inp == "arm":
            arm()
            break   
        else:
            pi.set_servo_pulsewidth(ESC,inp)

def calibrate():   #This is the auto calibration procedure of a normal ESC
    pi.set_servo_pulsewidth(ESC, 0)
    print("Disconnect the battery and press Enter")
    inp = raw_input()
    if inp == '':
        pi.set_servo_pulsewidth(ESC, max_value)
        print("Connect the battery NOW.. you will here two beeps, then wait for a gradual falling tone then press Enter")
        inp = raw_input()
        if inp == '':            
            pi.set_servo_pulsewidth(ESC, min_value)
            print "Wierd eh! Special tone"
            time.sleep(7)
            print "Wait for it ...."
            time.sleep (5)
            print "Im working on it, DONT WORRY JUST WAIT....."
            pi.set_servo_pulsewidth(ESC, 0)
            time.sleep(2)
            print "Arming ESC now..."
            pi.set_servo_pulsewidth(ESC, min_value)
            time.sleep(1)
            print "See.... uhhhhh"
            control() # You can change this to any other function you want

def control(): 
    print "I'm Starting the motor, I hope its calibrated and armed, if not restart by giving 'x'"
    time.sleep(1)
    speed = 1500    # change your speed if you want to.... it should be between 700 - 2000
    print "Controls - a to decrease speed & d to increase speed OR q to decrease a lot of speed & e to increase a lot of speed"
    while True:
        pi.set_servo_pulsewidth(ESC, speed)
        inp = raw_input()

        if inp == "q":
            speed -= 100    # decrementing the speed like hell
            print "speed = %d" % speed
        elif inp == "e":    
            speed += 100    # incrementing the speed like hell
            print "speed = %d" % speed
        elif inp == "d":
            speed += 10     # incrementing the speed 
            print "speed = %d" % speed
        elif inp == "a":
            speed -= 10     # decrementing the speed
            print "speed = %d" % speed
        elif inp == "stop":
            stop()          #going for the stop function
            break
        elif inp == "manual":
            manual_drive()
            break
        elif inp == "arm":
            arm()
            break   
        else:
            print "WHAT DID I SAID!! Press a,q,d or e"

def arm(): #This is the arming procedure of an ESC 
    print "Connect the battery and press Enter"
    inp = raw_input()    
    if inp == '':
        pi.set_servo_pulsewidth(ESC, 0)
        time.sleep(1)
        pi.set_servo_pulsewidth(ESC, max_value)
        time.sleep(1)
        pi.set_servo_pulsewidth(ESC, min_value)
        time.sleep(1)
        control() 

def stop(): #This will stop every action your Pi is performing for ESC ofcourse.
    pi.set_servo_pulsewidth(ESC, 0)
    pi.stop()

#This is the start of the program actually, to start the function it needs to be initialized before calling... stupid python.    
inp = raw_input()
if inp == "manual":
    manual_drive()
elif inp == "calibrate":
    calibrate()
elif inp == "arm":
    arm()
elif inp == "control":
    control()
elif inp == "stop":
    stop()
else :
    print "Thank You for not following the things I'm saying... now you gotta restart the program STUPID!!"

如何利用 opencv 简单解释二维码的组成

起因

今天 rockets 问我, 是否能够将二维码里面的色块用 0 和 1 表示出来.

分析

我脑海里分析了一下,不知道是不是可以用 opencv 实现,就打开电脑尝试了一下,似乎是可以的.
思路, 首先通过 qrcode 生成一个图片,然后保存一下,通过opencv 导入图片,然后再判断 10x10 大小的块中是否全是白色,如果是白色,就写一个 1
,如果是黑色,就写个 0. 然后遍历整个图片的 shape.

Talk is cheap, show me the code


import qrcode
import cv2
import numpy as np

url = 'https://www.dfrobot.com/product-2480.html'
file_name = 'qrcode.png'

def generate_image(url):
    qr = qrcode.QRCode()
    qr.add_data(url)
    qr.make(fit=False)
    img = qr.make_image(fill_color="black", back_color="white")
    img.save(file_name)

generate_image(url)
img1 = cv2.imread(file_name)
scale_percent = 80
width = int(img1.shape[1] * scale_percent / 100)
height = int(img1.shape[0] * scale_percent / 100)
dim = (width, height)

resized_img = cv2.resize(img1, dim, interpolation=cv2.INTER_AREA)
# gray_img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
for i in range(0, int(resized_img.shape[0]), 10):
    for j in range(0, int(resized_img.shape[1]), 10):
        if np.mean(resized_img[i:i+10, j:j+10]) == 255:
            cv2.putText(resized_img, '0', (i, j), cv2.FONT_HERSHEY_SIMPLEX, .2, (255, 0, 0))
        else:
            cv2.putText(resized_img, '1', (i, j), cv2.FONT_HERSHEY_SIMPLEX, .2, (255, 0, 0))

cv2.imshow('resized_img', resized_img)
cv2.waitKey(0)

执行效果:

重点

np.mean(图片数组) == 255的判断部分,可以判断图片是不是白色.

重构了一下,优化后:

import qrcode
import cv2
import numpy as np

# 定义生成 qrcode 的 url 链接和生成的文件名
url = 'https://www.dfrobot.com/product-2480.html'
file_name = 'qrcode.png'

def generate_image(url):
    """"
    生成二维码并保存
    """
    qr = qrcode.QRCode()
    qr.add_data(url)
    qr.make(fit=False)
    img = qr.make_image(fill_color="black", back_color="white")
    img.save(file_name)

def process_image(filename):
    """
    :param filename: 放入生成的二维码
    :return: 显示图
    """
    img1 = cv2.imread(file_name)
    scale_percent = 80
    width = int(img1.shape[1] * scale_percent / 100)
    height = int(img1.shape[0] * scale_percent / 100)
    dim = (width, height)
    resized_img = cv2.resize(img1, dim, interpolation=cv2.INTER_AREA)
    for i in range(0, int(resized_img.shape[0]), 10):
        for j in range(0, int(resized_img.shape[1]), 10):
            if np.mean(resized_img[i:i+5, j:j+5]) == 255:
                cv2.putText(resized_img, '0', (i+5, j+5), cv2.FONT_HERSHEY_SIMPLEX, .2, (255, 0, 0))
            else:
                cv2.putText(resized_img, '1', (i+5, j+5), cv2.FONT_HERSHEY_SIMPLEX, .2, (255, 0, 0))

    cv2.imshow('resized_img', resized_img)
    cv2.waitKey(0)

if __name__ == "__main__":
    generate_image(url)
    process_image(file_name)

优化后结果:

感觉还是不太对.

继续优化试试看.

import qrcode
import cv2
import numpy as np

# 定义生成 qrcode 的 url 链接和生成的文件名
url = 'https://www.dfrobot.com/product-2480.html'
file_name = 'qrcode.png'

def generate_image(url):
    """"
    生成二维码并保存
    """
    qr = qrcode.QRCode()
    qr.add_data(url)
    qr.make(fit=False)
    img = qr.make_image(fill_color="black", back_color="white")
    img.save(file_name)

def process_image(filename):
    """
    :param filename: 放入生成的二维码
    :return: 显示图
    """
    img1 = cv2.imread(file_name)
    scale_percent = 200
    width = int(img1.shape[1] * scale_percent / 100)
    height = int(img1.shape[0] * scale_percent / 100)
    dim = (width, height)
    resized_img = cv2.resize(img1, dim, interpolation=cv2.INTER_AREA)
    resized_img = cv2.cvtColor(resized_img, cv2.COLOR_BGR2GRAY)
    for i in range(0, int(resized_img.shape[0]), 10):
        for j in range(0, int(resized_img.shape[1]), 10):
            if np.mean(resized_img[i:i+3, j:j+3]) == 255:
                cv2.putText(resized_img, '1', (i + 5, j + 5), cv2.FONT_HERSHEY_SIMPLEX, .2, (0, 0, 255))
            else:
                cv2.putText(resized_img, '0', (i + 5, j + 5), cv2.FONT_HERSHEY_SIMPLEX, .2, (255, 0, 0))

    cv2.imshow('img', resized_img)
    cv2.waitKey(0)

if __name__ == "__main__":
    generate_image(url)
    process_image(file_name)

输出结果:

防止树莓派自动黑屏

前言

树莓派在运行桌面环境的时候,长时间不操作就会自动黑屏,是电源管理的一个节能的功能,有时候挺烦人,可以关闭它。

暂时关闭

在终端中输入:

sudo xset s off
sudo xset -dpms
sudo xset s noblank

永久关闭

  1. 通过sudo raspi-config 命令
    找到Display Options 然后选择Screen Blanking, 问是否启用,选择NO, 然后完成后重启树莓派。
  2. 通过编辑/etc/lightdm/lightdm.conf
    将xserver-command=X 前面的#去掉,然后改成:

    xserver-command=X -s 0 -dpms

    保存后退出重启系统。

    总结

    屡试不爽!

[Raspberry Pi 新系统]树莓派更新失败处理方法

故障现象:

pi@raspberrypi:~ $ sudo apt-get update
Get:1 http://archive.raspberrypi.org/debian buster InRelease [32.6 kB]
Get:2 http://raspbian.raspberrypi.org/raspbian buster InRelease [15.0 kB]
Reading package lists... Done
E: Repository 'http://raspbian.raspberrypi.org/raspbian buster InRelease' changed its 'Suite' value from 'stable' to 'oldstable'
N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.
E: Repository 'http://archive.raspberrypi.org/debian buster InRelease' changed its 'Suite' value from 'testing' to 'oldstable'
N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.

修复方法:

sudo apt-get -y update --allow-releaseinfo-change

结果如下:

Get:1 http://archive.raspberrypi.org/debian buster InRelease [32.6 kB]
Get:2 http://raspbian.raspberrypi.org/raspbian buster InRelease [15.0 kB]
Get:3 http://raspbian.raspberrypi.org/raspbian buster/contrib Sources [78.5 kB]
Get:4 http://archive.raspberrypi.org/debian buster/main armhf Packages [393 kB]
Get:5 http://raspbian.raspberrypi.org/raspbian buster/main Sources [11.4 MB]
Ign:5 http://raspbian.raspberrypi.org/raspbian buster/main Sources
Get:6 http://raspbian.raspberrypi.org/raspbian buster/non-free Sources [139 kB]
Get:7 http://raspbian.raspberrypi.org/raspbian buster/rpi Sources [1,132 B]
Get:8 http://raspbian.raspberrypi.org/raspbian buster/main armhf Packages [13.0 MB]
21% [8 Packages 443 kB/13.0 MB 3%]

顺利解决~

树莓派摄像头-追踪

今天实现了对物体检测并控制舵机运动的应用。
舵机使用了pca9685控制,因此备份一下pac9685舵机控制板的驱动:

PCA9685.py

import time
import math

class PWM:
    _mode_adr      = 0x00
    _base_adr_low  = 0x08
    _base_adr_high = 0x09
    _prescale_adr  = 0xFE

    def __init__(self, bus, address = 0x40):
        '''
        Creates an instance of the PWM chip at given i2c address.
        @param bus: the SMBus instance to access the i2c port(0 or 1).
        @param address: the address of the i2c chip (default: 0x40).
        '''
        self.bus = bus
        self.address = address
        self._writeByte(self._mode_adr, 0x00)

    def setFreq(self, freq):
        '''
        Sets the PWM frequency. The value is stored in the device.
        @param freq: the frequency in Hz (approx.)
        '''
        prescaleValue = 25000000.0  # 25MHz
        prescaleValue /= 4096.0     # 12-bit
        prescaleValue /= float(freq)
        prescaleValue -= 1.0  
        prescale = math.floor(prescaleValue + 0.5)
        oldmode = self._readByte(self._mode_adr)
        if oldmode == None:
            return
        newmode = (oldmode & 0x7F) | 0x10
        self._writeByte(self._mode_adr, newmode)
        self._writeByte(self._prescale_adr, int(math.floor(prescale)))
        self._writeByte(self._mode_adr, oldmode)
        time.sleep(0.005)
        self._writeByte(self._mode_adr, oldmode | 0x80)

    def setDuty(self, channel, duty):
        '''
        Sets a single PWM channel. The value is stored in the device.
        @param channel: one of the channels 0..15
        @param duty: the duty cycle 0..100
        '''
        data = int(duty * 4096 /100)  # 0..4096 (included)
        self._writeByte(self._base_adr_low + 4 * channel, data & 0xFF)
        self._writeByte(self._base_adr_high + 4 * channel, data >> 8)

    def _writeByte(self, reg, value):
        try:
            self.bus.write_byte_data(self.address, reg, value)
        except:
            print("Error while writing to I2C device")

    def _readByte(self, reg):
        try:
            result = self.bus.read_byte_data(self.address, reg)
            return result
        except:
            print("Error while Reading from I2C device")
            return None

调用方法简单:

import time
from smbus2 import SMBus
from pca9685 import PWM

freq = 50
addr = 0x40
channels = [0, 1, 2] 
a = 12.5
b = 2

bus = SMBus(1)
pwm = PWM(bus, addr)
pwm.setFreq(freq)

def setPos(channel, pos):
    duty = a / 180 * pos + b 
    pwm.setDuty(channel, duty)
    time.sleep(0.1)

while True:
    try:
        for pos in range(60, 120, 2):
            setPos(channels[0], pos)
            time.sleep(0.1)

        for pos in range(60, 120, 3):
            setPos(channels[1], pos)
            time.sleep(0.5)

        for pos in range(0, 90, 3):
            setPos(channels[2], pos)
            time.sleep(0.01)

    except KeyboardInterrupt:
        for i in channels:
            setPos(i, 0)
        break

OpenCV 通过颜色检测并控制:

import cv2
import time
import numpy as np
from pca9685 import PWM
from smbus2 import SMBus

freq = 50
addr = 0x40
ch1  = 0
ch2  = 1

bus = SMBus(1)
pwm = PWM(bus, addr)
pwm.setFreq(freq)

def setPos(channel, position):
    pwm.setDuty(channel, position)
    time.sleep(0.01)

def posMap(x, in_min, in_max, out_min, out_max):
    return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

cap = cv2.VideoCapture(0)
cap.set(3, 320)
cap.set(4, 160)
ret, frame = cap.read()

cx = int(frame.shape[1] / 2)
center = int(frame.shape[1] / 2)
pos = 90

while True:
    ret, frame = cap.read()
    frame = cv2.flip(frame, 1)
    hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    ly = np.array([51, 0, 0])
    hy = np.array([180, 255, 255])

    mask = cv2.inRange(hsv_frame, ly, hy)
    contours, hir = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
    # cv2.drawContours(frame, contours[1], -1, (0, 0, 255), 2)
    for cnt in contours[1:]:
        (x, y, w, h) = cv2.boundingRect(cnt)
        cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 255,0), 2)
        cx = int((x + x + w)/ 2)
        # cv2.drawContours(frame, cnt, -1, (0, 0, 255), 2)
        cv2.line(frame, (cx, 0), (cx, frame.shape[0]), (0, 0, 255), 1)
        if cx < center:
            pos -= 1.5
        elif cx > center:
            pos += 1.5  
        # pos = posMap(pos, 1, 480, 60, 120)
        setPos(ch1, pos)

        break
    # mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
    # hstack = np.hstack([mask, frame])
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()
setPos(ch1, 0)

总结

就是控制dutyCycle,频率肯定要50hz,一般的多级可能需要a, b函数作为微调。根据自己的舵机微调a和b的值。

可以尝试 a 设置8.5, b设置2

OpenWRT 在树莓派启用WiFi 如此简单!

登陆树莓派CM4, 如果发现wlan0找不到,无法初始化,就这样执行以下命令:

cd /lib/firmware/brcm
cp brcmfmac43455-sdio.raspberrypi,4-model-b.txt brcmfmac43455-sdio.raspberrypi,4-compute-module.txt
reboot

然后尝试改一下wifi-country的信息。

iw reg get 
iw reg set CN
iw reg get 
lsmod |grep brcmfmac

接着尝试用

iw dev
wifi status

看看状态。

uci show wireless

这里需要启用radio0, 并且把AP模式改成STA模式。

uci set wireless.radio0.disabled='0'
uci set wireless.default_radio0.mode='sta'
uci set wireless.default_radio0.network='wan'
uci show wireless

树莓派安装windows11 预览版遇到的问题总结

在最近发现windows11 预览版arm64位系统发布后,可以通过一个cmd的脚本生成需要的镜像,但是也会产生一个非常大容量的目录: MountUUP,应该是通过UUPtoISO生成的临时文件,非常巨大,包含了很多cab的包还有一些wim的东西,在执行删除的时候总是提示你需要trustinstaller的权限,无法删除,会占用大量磁盘,实际上是因为它挂载了一个镜像文件install.wim, 我想要删除它清空磁盘空间,找了一圈,发现可以通过先取消挂载然后再删除就顺利干掉了。
下面是一段网上查到的资料原文。
The following DISM command will look for stale mountpoints on all drives and (attempt to) unmount them:

DISM.exe /cleanup-wim

Or, you can do it manually:

DISM.exe /Unmount-WIM /mountdir=C:\MountUUP /discard

管理员打开cmd窗口,执行上述命令后删除相应目录即可。问题解决。

Ubuntu 上安装 Cacti 现在这么简单了么?

前言

最近讲监控, 又将很久以前的东西重新拾起来, 尝试在我的小 mac 上的虚拟机里面进行各种安装测试.

安装 SNMP

我以前的印象中,安装 snmp 需要用的软件包的名字是: net-snmp,但是在 ubuntu 上我发现,就直接安装snmp 和 snmpd 好像就行了.

   sudo apt-get -y install snmp snmpd 

安装 cacti

我之前的经验是需要安装一套 LAMP 架构.先.
但是发现 ubuntu 上面已经让我可以变得非常懒了, 直接一条命令就都给你安装上来了.

sudo apt-get -y install cacti

顺手更新了一把系统.

疑惑

官方网站上说 cacti 账户和密码默认是 admin,但是好像不行.真是折腾.
后来用了账户 admin,密码用的数据库的密码才正常登陆.

Mac OS 上虚拟机无法联网的临时修复方法记录

事情的起因

由于要上课,因此安装 centos8.3.2011一个短命的系统,但是网卡不好用.

实际的电脑信息

我电脑是:

然后我的 parallel 的虚拟机网卡不好用, 尝试联网各种失败,开机无法联网,CentOS 也不好用.

解决方法:

于是搜了半天, 尝试了半天,终于找到一个看似能用的方法:
->关闭所有VMS和Parallels Desktop-

打开终端并键入:sudo -b /Applications/Parallels\Desktop.app/Contents/MacOS/prl_client_app

总结:

Parallels将打开,也许您需要打开您的VM再次...网络将正常工作...
这是一个临时解决方案,而官方的并行支持不起作用..

浪派-造物志

何谓:浪派?

所谓浪派,其实是我和几个朋友一起搞的一个树莓派小车的项目,其核心内容是想在疫情期间无法面基就只好通过网络交互来实现原先的自由自造的折腾行为.其中涉及到原型设计,芯片选型,PCB设计,打样焊接,程序调试等一系列技术栈,在设计之初为了能够让大家都有参与进来的兴趣,还专门采用了kicad设计软件来进行pcb设计...参与的小伙伴各显神通,但是也遭遇过一些小小的滑铁卢...

回忆杀


未完待续,手机更新大拇指压力太大了....

Linux内核编译如果出问题了怎么办?

如果出问题了

如果您遇到的问题似乎是由于内核错误引起的,请检查文件维护者以查看是否有特定人员与您遇到麻烦的内核部分相关联。如果没有列出有任何人,那么第二个最好的办法是将它们邮寄给(torvalds@linux-foundation.org),以及可能的其他任何有关的邮件列表或新闻组。

在所有错误报告中,请告诉您您在操作的是什么内核,如何复制问题以及您的设置是什么(使用常识)。如果问题是新问题,请告诉他,如果问题是旧问题,请在首次发现问题时告诉他,他才是Linux内核最大的BOSS。

例如:
如果该错误导致显示如下消息:

unable to handle kernel paging request at address C0000010
Oops: 0002
EIP:   0010:XXXXXXXX
eax: xxxxxxxx   ebx: xxxxxxxx   ecx: xxxxxxxx   edx: xxxxxxxx
esi: xxxxxxxx   edi: xxxxxxxx   ebp: xxxxxxxx
ds: xxxx  es: xxxx  fs: xxxx  gs: xxxx
Pid: xx, process nr: xx
xx xx xx xx xx xx xx xx xx xx

或屏幕上或系统日志中的类似内核调试信息,请准确复制。转储对您来说似乎令人难以理解,但是它确实包含有助于调试问题的信息。转储上方的文本也很重要:它说明了为什么内核转储代码的原因(在上面的示例中,这是由于内核指针错误引起的)。有关寻找转储的更多信息,请参见“错误查找”

如果使用CONFIG_KALLSYMS编译内核,则可以按原样发送转储,否则,您将不得不使用该ksymoops程序来理解转储(但通常首选使用CONFIG_KALLSYMS进行编译)。可以从https://www.kernel.org/pub/linux/utils/kernel/ksymoops/下载该实用程序 。另外,您可以手动进行转储查找:

在上面的调试转储中,如果您可以查找EIP值的含义,那么它将大有帮助。这样的十六进制值对我或其他人没有太大帮助:它将取决于您的特定内核设置。您应该做的是从EIP行中获取十六进制值(忽略0010:),然后在内核名称列表中查找该值,以查看哪个内核函数包含有问题的地址。

要找出内核函数名称,您需要找到与表现出该症状的内核相关的系统二进制文件。这是文件“ linux/vmlinux”。要提取名称列表并将其与内核崩溃时的EIP匹配,请执行以下操作:

nm vmlinux | sort | less

这将为您提供按升序排序的内核地址列表,从中可以轻松找到包含违规地址的函数。请注意,内核调试消息给出的地址不一定与功能地址完全匹配(实际上,这不太可能),因此您不能只是“ grep”列表:但是,列表将为您提供每个内核函数的起始点,因此,通过查找起始地址比您要查找的起始地址低的函数,然后查找具有较高地址的函数,可以找到所需的起始地址。实际上,在问题报告中包括一些“上下文”可能是个好主意,在有趣的地方加上几行即可。

如果由于某种原因您不能执行上述操作(您有预编译的内核映像或类似文件),请尽可能多地告诉他有关您的设置的信息。有关详细信息,请阅读“报告问题”。

另外,您可以在运行的内核上使用gdb。(只读;即,您不能更改值或设置断点。)为此,首先使用-g编译内核;适当地编辑arch/x86/Makefile,然后执行。您还需要启用CONFIG_PROC_FS(通过)。

make cleanmake config

使用新内核重新引导后,请执行。现在,您可以使用所有常用的gdb命令。查找系统崩溃点的命令是。(用EIP值替换XXXes。)

gdb vmlinux /proc/kcorel *0xXXXXXXXX

当前,对非运行内核进行gdb操作失败,因为gdb(错误地)无视了为其编译内核的起始偏移量。

Linux内核编译前需要了解的内容

 Linux内核版本5.x

  • < http://kernel.org/ >
    这些是Linux版本5的发行说明。请仔细阅读它们,因为它们告诉您所有内容,解释如何安装内核以及出现问题时应采取的措施。

什么是Linux?

Linux是Unix操作系统的克隆,由Linus Torvalds在网络松散的黑客团队的协助下从零开始编写。它旨在实现POSIX和Single UNIX规范的合规性。

它具有您在现代的成熟Unix中所期望的所有功能,包括真正的多任务,虚拟内存,共享库,需求加载,共享的写时复制可执行文件,适当的内存管理以及包括IPv4和IPv6在内的多堆栈网络。

它根据GNU通用公共许可证v2分发-有关更多详细信息,请参见随附的COPYING文件。

 它在什么硬件上运行?

尽管最初最初是为基于32位x86的PC(386或更高版本)开发的,但今天的Linux也可以(至少)在Compaq Alpha AXP,Sun SPARC和UltraSPARC,Motorola 68000,PowerPC,PowerPC64,ARM,Hitachi SuperH,Cell上运行,IBM S / 390,MIPS,HP PA-RISC,英特尔IA-64,DEC VAX,AMD x86-64 Xtensa和ARC体系结构。

只要具有分页内存管理单元(PMMU)和GNU C编译器(gcc)的端口(属于GNU编译器集合,GCC的一部分),Linux便可以轻松移植到大多数通用的32位或64位体系结构中。 。尽管功能显然受到限制,但Linux也已移植到许多没有PMMU的体系结构中。Linux也已移植到其自身。现在,您可以将内核作为用户空间应用程序运行-这称为UserMode Linux(UML)。

文献资料

互联网上和书籍中都有许多电子形式的文档,既特定于Linux,又涉及一般的UNIX问题。我建议您查看任何Linux FTP站点上的LDP(Linux文档项目)书籍的文档子目录。本README并不是系统上的文档:有更好的可用资源。
Documentation /子目录中有各种README文件:例如,这些文件通常包含某些驱动程序的特定于内核的安装说明。

安装内核源文件

如果安装完整源代码,请将内核tarball放在您具有权限的目录中(例如,主目录)并解压缩它:

xz -cd linux-5.x.tar.xz | tar xvf -

将“ X”替换为最新内核的版本号。

不要使用/usr/src/linux 路径!该区域具有(通常不完整)一组由库的头文件使用的内核头文件。它们应该与库匹配,并且不会因kernel-du-jour碰巧而陷入混乱。

您也可以通过补丁在5.x版本之间升级内核。补丁以xz格式分发。要通过补丁安装,请获取所有较新的补丁文件,输入内核源代码的顶层目录(linux-5.x)并执行:

xz -cd ../patch-5.x.xz | patch -p1

对于大于当前源代码树in_order版本“ x”的所有版本替换“ x” ,您应该可以。您可能要删除备份文件(some-file-name〜或some-file-name.orig),并确保没有失败的补丁程序(some-file-name#或some-file-name.rej)。 。如果有,则您或我都犯了一个错误。

与5.x内核的修补程序不同,5.xy内核(也称为-stable内核)的修补程序不是增量的,而是直接应用于基本5.x内核。例如,如果您的基本内核是5.0,并且您想应用5.0.3补丁程序,则一定不要首先应用5.0.1和5.0.2补丁程序。同样,如果您正在运行5.0.2内核版本并希望跳至5.0.3,则必须先反转5.0.2补丁(即补丁-R),然后再应用5.0.3补丁。

另外,脚本patch-kernel可用于自动执行此过程。它确定当前的内核版本并应用找到的所有修补程序:

linux/scripts/patch-kernel linux

上面命令中的第一个参数是内核源的位置。打补丁的程序是从当前目录的应用程序,但是可以将备用目录指定为第二个参数。

  • 确保您周围没有旧的.o文件和依赖项:
    cd linux
    make mrproper

    您现在应该正确安装了内核源码。

 软件需求

编译和运行5.x内核需要各种软件包的最新版本。请教 编译内核所需的最低版本号的最低要求以及如何获取这些软件包的更新。请注意,使用这些软件包的旧版本可能会导致间接错误,很难跟踪,因此请不要假定您仅在构建或操作过程中出现明显问题时才可以更新软件包。

 内核的构建目录

默认情况下,编译内核时,所有输出文件将与内核源代码一起存储。使用该选项,您可以为输出文件(包括.config)指定备用位置。例:

make O=output/dir
kernel source code: /usr/src/linux-5.x
build directory:    /home/name/build/kernel

要配置和构建内核,请使用:

cd /usr/src/linux-5.x
make O=/home/name/build/kernel menuconfig
make O=/home/name/build/kernel
sudo make O=/home/name/build/kernel modules_install install
  • 请注意:如果使用该O=output/dir选项,则必须将其用于所有的make调用。

配置内核

即使仅升级一个次要版本,也不要跳过此步骤。每个版本中都添加了新的配置选项,如果未按预期设置配置文件,则会出现奇怪的问题。如果要以最少的工作量将现有配置带到新版本,请使用,它只会询问您新问题的答案。

make oldconfig

备用配置命令是:

"make config"      Plain text interface.
"make menuconfig"  Text based color menus, radiolists & dialogs.
"make nconfig"     Enhanced text based color menus.
"make xconfig"     Qt based configuration tool.
"make gconfig"     GTK+ based configuration tool.
"make oldconfig"   Default all questions based on the contents of
                   your existing ./.config file and asking about
                   new config symbols.
"make olddefconfig"
                   Like above, but sets new symbols to their default
                   values without prompting.
"make defconfig"   Create a ./.config file by using the default
                   symbol values from either arch/$ARCH/defconfig
                   or arch/$ARCH/configs/${PLATFORM}_defconfig,
                   depending on the architecture.
"make ${PLATFORM}_defconfig"
                   Create a ./.config file by using the default
                   symbol values from
                   arch/$ARCH/configs/${PLATFORM}_defconfig.
                   Use "make help" to get a list of all available
                   platforms of your architecture.
"make allyesconfig"
                   Create a ./.config file by setting symbol
                   values to 'y' as much as possible.
"make allmodconfig"
                   Create a ./.config file by setting symbol
                   values to 'm' as much as possible.
"make allnoconfig" Create a ./.config file by setting symbol
                   values to 'n' as much as possible.
"make randconfig"  Create a ./.config file by setting symbol
                   values to random values.
"make localmodconfig" Create a config based on current config and
                      loaded modules (lsmod). Disables any module
                      option that is not needed for the loaded modules.

                      To create a localmodconfig for another machine,
                      store the lsmod of that machine into a file
                      and pass it in as a LSMOD parameter.

                      Also, you can preserve modules in certain folders
                      or kconfig files by specifying their paths in
                      parameter LMC_KEEP.

              target$ lsmod > /tmp/mylsmod
              target$ scp /tmp/mylsmod host:/tmp

              host$ make LSMOD=/tmp/mylsmod \
                      LMC_KEEP="drivers/usb:drivers/gpu:fs" \
                      localmodconfig

                      The above also works when cross compiling.

"make localyesconfig" Similar to localmodconfig, except it will convert
                      all module options to built in (=y) options. You can
                      also preserve modules by LMC_KEEP.
"make kvmconfig"   Enable additional options for kvm guest kernel support.
"make xenconfig"   Enable additional options for xen dom0 guest kernel
                   support.
"make tinyconfig"  Configure the tiniest possible kernel.

您可以在Kconfig make config中找到有关使用Linux内核配置工具的更多信息。

关于的注释:make config

拥有不必要的驱动程序会使内核变大,并且在某些情况下可能会导致问题:检测不存在的控制器可能会使您的其他控制器卡住。
如果存在带有编译的数学仿真的内核,则仍将使用协处理器:在这种情况下,永远不会使用数学仿真。内核会稍大一些,但是无论它们是否具有数学协处理器,它都可以在不同的机器上工作。
“内核hacking”配置细节通常会导致更大或更慢的内核(或同时出现这两者),并且甚至可以通过配置一些例程来主动尝试破坏坏代码以发现内核问题(kmalloc()),从而降低内核的稳定性。因此,您可能应该对“开发”,“实验”或“调试”功能的问题回答“ n”。

 编译内核

确保您至少有可用的gcc 4.9。有关更多信息,请参阅编译内核的最低要求。

请注意,您仍然可以使用此内核运行a.out用户程序。

执行make以创建压缩的内核映像。如果您已经安装了适合内核makefile的lilo,也可以这样做,但是您可能需要首先检查特定的lilo设置。

make install

要进行实际安装,您必须是root用户,但是任何正常的构建都不需要这样做。

如果将内核的任何部分都配置为modules,则还必须这样做。

make modules_install

详细的内核编译/生成输出:

通常,内核构建系统以相当安静的模式运行(但不是完全安静)。
但是,有时您或其他内核开发人员需要在执行时完全看到编译,链接或其他命令。
为此,请使用“详细”构建模式。这可以通过传递V=1给make命令来完成 ,例如:

make V=1 all

要使构建系统还告诉您重建每个目标的原因,请使用V=2。默认值为V=0

  • 万一出现问题,请随时准备备份内核。
    对于开发版本尤其如此,因为每个新版本都包含尚未调试的新代码。
    确保还保留了与该内核对应的模块的备份。
    如果要安装与工作内核具有相同版本号的新内核,请在执行之前对modules目录进行备份。

    make modules_install

    另外,在编译之前,请使用内核配置选项LOCALVERSION将唯一的后缀附加到常规内核版本中。可以在常规设置菜单中设置LOCALVERSION

为了引导您的新内核,您需要将内核映像(例如,编译后的…/linux/arch/x86/boot/bzImage)复制到找到常规可引导内核的位置。

** 不再支持在没有引导加载程序(例如LILO)帮助的情况下直接从软盘引导内核。

如果从硬盘驱动器引导Linux,则有可能使用LILO,LILO使用文件/etc/lilo.conf中指定的内核映像。内核映像文件通常是/vmlinuz,/boot/vmlinuz,/ bzImage或/boot bzImage。
要使用新内核,请保存旧内核的副本,然后将新内核镜像复制到旧内核镜像上。然后,您必须重新运行LILO才能更新加载映射!否则,您将无法引导新的内核镜像。

  • 重新安装LILO通常是运行/sbin/lilo的问题。您可能希望编辑/etc/lilo.conf为旧内核映像(例如/vmlinux.old)指定一个条目,以防新内核映像不起作用。有关更多信息,请参见LILO文档。

  • 重新安装LILO之后,您应该已经准备就绪。

  • 关闭系统,重新启动,然后享受!

如果您需要在内核映像中更改默认的根设备,视频模式等,请在适当的地方使用引导加载程序的引导选项。无需重新编译内核即可更改这些参数。

  • 使用新内核重新启动并开始浪吧!