Ubuntu上Python教程

Python中常见的括号(),[],{}的区别:

Python中的这三种数据类型有不同的作用,分别代表不同的Python基本内置数据类型。

1 () tuple元组数据类型

元组是一种不可变序列,

1
2
3
4
5
6
7
>>> tup = (1,2,3)
>>> tup
(1, 2, 3)
>>> ()
()
>>> 55,
(55,)

2 []列表数据类型

列表是一种可变序列,

1
2
3
4
5
6
>>> list('Python')
['P', 'y', 't', 'h', 'o', 'n']
>>> list('ABC')
['A', 'B', 'C']
>>> list(['ABC'])
['ABC']

3 {}字典数据类型

字典是Python中唯一内建的映射类型

1
2
3
>>> dic = {'jon':'boy','lili':'girl'}
>>> dic
{'jon': 'boy', 'lili': 'girl'}

Python下载第三方模块,下载速度慢,如何换源

‘’’
mkdir ~/.pip
vim ~/.pip/pip.conf
‘’’

‘’’
[global]
index-url = http://pypi.douban.com/simple
[install]
use-mirrors =true
mirrors =http://pypi.douban.com/simple/
trusted-host =pypi.douban.com
‘’’

不变数据类型和可变数据类型

可变数据类型:value值改变,id值不变;
不变数据类型:value值改变,id值也改变。

分辨方式:在改变value值的同时,使用id()函数来查看变量id值是否发生变化。

不变 整型 浮点型 字符串 元组
可变 列表 字典

不变和可变数据类型对运算结果的影响:

不变

1
2
3
4
5
6
a = 1
b = a
b += 1

print(a)
print(b)

Ans

1
2
1
2

可变

1
2
3
4
5
6
a = {'name':'jack'}
b = a
b['age'] = 27

print(a)
print(b)

Ans

1
2
{'name':'jack','age':27}
{'name':'jack','age':27}

简而言之,对于不可变数据类型,当多个名称包含的内容都是相同的,当改变一个之后,其他的不变,改变的指向另外的地址。
对于可变的数据类型,当多个内容相同,改变一个之后,所有指向的地址不变,但是内容改变了。

打包可执行文件

通过pyinstaller将python程序打包为可执行文件。

pyinstaller,distribute分别是打包所需要的通过pip3来安装

1
2
3
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple distribute

pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller

注意,有时候会遇到问题,当执行打包好的文件时,

1
2
3
4
5
no module named 'pkg_resources.py2_warn'
//这个可以通过下面的方法来解决
pip uninstall setuptools
pip install setuptools==44.0.0
//这是将setuptools进行降级处理,可能会出现有些功能无法使用的问题

还有一个解决方法记录一下:
1.先用pyinstaller -D(F) xxx.py生成一下(不一定能正常运行)
2.(关键)经过第一步之后,目录下有个.spec文件,用记事本打开,里面有个hiddenimports,在这条里面加上pkg_resources.py2_warn

1
hiddenimports=['pkg_resources.py2_warm']

3.再次用pyinstaller,注意这时候输入的命令是pyinstaller -D(F) xxx.spec
4.经过步骤2就可以解决这个问题,若仍然提示no module named XXXXX ,则再次写入到hiddenimports
5.需要经过几次调试,建议先用-D处理没问题之后再-F。

Python中关键词

name

name__是python的一个内置类属性,它存储模块的名称。
python的模块既可以被调用,也可以独立运行。而被调用时__name__存储的是py文件名(模块名称),独立运行时存储的是”__main
“。
那么它的作用主要就是用来区分,当前模块是独立运行还是被调用。

具体应用
当我们建立一个模块的时候,我们会在该模块最后输出调试信息,进行调试,调试之后,会在其他地方导入这个模块,但是,我们在调用这个模块的时候不希望产生调试信息,所以我们就需要区分这个模块是独立运行还是被调用,被调用的情况下就不输出调试信息。

例如:

1
2
if __name__ == '__main__':
...

file

查看模块的源文件路径,当指定模块或者包没有说明文档的时候,仅仅通过help()函数或者__doc__属性无法帮助理解该模块的具体功能,我们通过__file__查找模块的具体位置,直接查看源代码。

1
2
import string
print(string.__file__)

注意,不是所有的模块都提供__file__属性,因为并不是所有的模块的实现都是采用的python语言,有的模块是使用的其他编程语言,例如c语言

子模块导入子模块中import

当我们使用一个子模块的时候,子模块会调用其他子模块,这时候,使用下面的方法:

1
from .xxx import xxx

但是,在检测子模块的子模块的时候,上面的方法会报错,就可以直接使用:

1
from xxx import xxx

Ubuntu上opencv教程3之核心操作

0.简介

1.对图片的基本操作
读取和修改像素数值,选取部分图片区域
2.对图片的算数操作
3.计算改进技术
提高运算效率
4.数学运算工具
opencv中的数学工具例如PCA,SVD等

1.图片的基本操作

1.1 获取和修改像素值

1
2
3
4
5
6
7
8
9
10
11
12
13
import cv2
import numpy as np

img = cv2.imread('pic.jpg')

px = img[100,100]
print(px)

blue = img[100,100,0]
print(blue)

img[100,100] = [255,255,255]
print(img[100,100])

返回

1
2
3
[157 166 200]
157
[255 255 255]

注意:
numpy是一个快速数组计算的库,所以简单的获取和修改单个像素的值都是缓慢的,不建议如此使用。

上面的方法通常用来选择数组的一个区域,例如前5行和后3列。
对于单个像素的获取,通常用array.item()和array.itemset(),但是它总是返回一个数值,所以如果你想要获取所有BGR的值,就需要分别获取。

1
2
3
4
5
6
#获取 RED 值
img.item(10,10,2)

#修改 RED 值
img.itemset((10,10,2),100)
img.item(10,10,2)

1.2 获取图片的属性

图片属性包括:行,列,通道,图片数据的类型,像素的数目等等。

1
2
3
4
5
6
7
8
print(img.shape)
#获取图片的行,列,通道

print(img.size)
#获取图片中的像素数目

print(img.dtype)
#获取图片的数据类型

图片的数据类型是非常重要的,因为很多的时候错误原因就是不合适的数据类型。

1.3 选取图片的区域 ROI(Region Of Image)

有时候,需要选取某些区域的图像,例如,对于眼睛的检测,需要首先执行面部检测,然后再面部内搜索眼睛,这种方法提高了准确性和性能。

所以,ROI是包含了numpy数组

1
2
>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball

1.4 分割和融合图片的通道

B,G,R是彩色图片的通道,对于这个的操作有

1
2
3
4
5
b,g,r = cv2.split(img)
img = cv2.merge((b,g,r))

b = img[:,:,0]
img[:,:,2] = 0

注意cv2.split()是一个时间花费昂贵的运算,只有必要的时候才会使用,一般情况下,numpy数组是更加有效的,并且尽可能用。

1.5 为图片制作边框(Padding)

Padding,内边框,填充,
如果你想在图片的周围制造边框,可以使用cv2.copyMakeBorder()函数,但是对复杂的运算有更多的应用,无内边框等,这个函数有下列的变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
src 
#输入图片
top,bottom,left,right
#在相对应的方向的边界的宽度,单位是像素数目。
borderType
/*
标志位用来确定添加哪种边界,有下列的类型
cv2.BORDER_CONSTANT 添加一个固定颜色的边界,下个变量应该是颜色
cv2.BORDER_REFLECT 边界将会镜像边界的元素,
例如:fedcba|abcdefgh|hgfedcb
cv2.BORDER_REFLECT_101
cv2.BORDER_DEFAULT 上面两个都是一样的,和上面的相似,但是有不同
例如: gfedcb|abcdefgh|gfedcba
cv2.BORDER_REPLICATE最后的元素代替边界
例如:aaaaaa|abcdefgh|hhhhhhh
cv2.BORDER_WRAP 就像:cdefgh|abcdefgh|abcdefg
*/
value
#如果边界类型是固定颜色的边界,那么需要变量来表示边界的颜色

这样,下面是例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2
import numpy as np
from matplotlib import pyplot as plt

BLUE = [255,0,0]

img1 = cv2.imread('opencv_logo.png')

replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)

plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()

上面有matplotlib,类似于matlab的绘制函数

2.图片的算数操作

2.1 图片加法

通过opencv的函数cv2.add()将两个图片相加,或者通过数组res=img1+img2。所有的图片必须有相同的类型和深度,或者第二个图片可以是一个灰度值。

但是这两个方法有区别:
opencv加法是饱和运算
numpy是取余运算
例如:

1
2
3
4
5
6
>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print cv2.add(x,y) # 250+10 = 260 => 255
[[255]]
>>> print x+y # 250+10 = 260 % 256 = 4
[4]

当你添加两个图片时候,opencv函数将会提供更好的结果,最好使用opencv函数。

2.2 图像混合

也是图片加法,但是不同的权重给图片,有混合和透明度的感觉。

1
g(x) = (1 - a)f0(x) + af1(x)

将a从0到1,可以将一个图片转换为另一个图片,可以用cv2.addWeighted()来进行图片的混合。
cv2.addWeighted(img1,a,img2,b,c)

out = a * img1 + b * img2 + c

1
2
3
4
5
6
7
8
img1 = cv2.imread('img.png')
img2 = cv2.imread('logo.jpg')

dst = cv2.addWeighted(img1,0.7,img2,0.3,0)

cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.3 位运算操作

and,or,not或者xor位运算,这些在抽取图片的不矩形部分中,非常有用。
将一个logo放在一个图片上,如果将两个图片直接相加,它将会修改颜色。如果将两个图片进行图像混合,就会有透明度。如果是一个矩形区域就可以直接用之前的ROI图片区域方法。可以通过位操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 加载两个图片
img1 = cv2.imread('img.jpg')
img2 = cv2.imread('logo.png')

# 将logo放在左上角,所以可以选取图片区域
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 将logo转变为灰度创立了一个logo的掩体,然后建立一个相反的掩体
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)

# 将logo相反的掩膜来提取图片中logo部分以外的数据
img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

# 用logo的掩膜来提取图片中logo的数据
img2_fg = cv2.bitwise_and(img2,img2,mask = mask)

# 将获取的数据进行相加,就可以获取修改之后的图片数据
dst = cv2.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst

cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()

其中有图片阈值函数,cv2.threshold()函数,当像素的数值大于某个阈值,像素点会被分配为一个数值,其他的分配另外的数值。第一个变量是图片,应该是灰度图。第二个变量是阈值,第三个变量是最大值,代表了如果像素值高于或者低于阈值的时候,所需要的数值,第四个变量是阈值函数的类型,分别有:

1
2
3
4
5
cv2.THRESH_BINARY   //像素值大于阈值,就修改为最大值,其他的为0
cv2.THRESH_BINARY_INV//像素值大于阈值为0,否则是最大值
cv2.THRESH_TRUNC //像素值大于阈值则为阈值,否则是原本的数值
cv2.THRESH_TOZERO //像素值大于阈值则不变,否则为0
cv2.THRESH_TOZERO_INV//像素值大于阈值为0,否则不变

备注:
BINARY模式,阈值的最大值是255,也就是白色,特别注意在opencv中,0代表黑色。
TOZERO模式,将部分数值变为0
INV模式,就是相反

之后是cv2.bitwise_and()函数,这里面有一个掩膜,例如
out = cv2.bitwise_and(img1,img2,mask = mask_defined)
首先,img1和img2有相同的大小,而且
out[i] = img1[i] & img2[i] if mask[i] != 0

3.改进运算效率的技术

在图片的运算中,我们每秒需要处理大量的运算,所以,代码不仅仅需要得到正确的解法,而且具有很高的效率,这一部分包含下面的内容:
测量代码运行的效率
提高代码效率的一些小诀窍
需要使用下面的一部分函数:cv2.getTickCount,cv2.getTickFrequency。
除了opencv,python中有time模块,可以用来测量执行的时间,以及profile模块来得到代码的详细内容,例如,代码中每个功能所需要的时间,函数被调用了多少次等。但是,如果你使用IPython,这些特性都很好的被包含在内。

3.1 使用opencv来测量性能

cv2.getTickCount函数用来返回一个相关事件(例如,机器启动的时刻)之后直到这个函数被调用之间的时钟周期数,所以,你可以在函数前后调用这个函数来计算得到这个函数的执行需要多少个时钟周期。

cv2.getTickFrequency函数返回了时钟周期频率,或者可以说每秒的时钟周期数目,所以可以通过下面的代码获取执行时间

1
2
3
4
e1 = cv2.getTickCount()
# your code execution
e2 = cv2.getTickCount()
time = (e2 - e1)/ cv2.getTickFrequency()

你可以直接使用time模块中的time.time()函数,取两个函数返回值的差大概。没有用过。

3.2 opencv中默认的优化

opencv中的许多函数都通过SSE2,AVX等进行了优化,它还包含了没有优化的代码。所以如果我们的系统支持这些特性,我们应该使用他们,几乎大多数现代处理器都支持他们。在编译的时候这些就默认使用了。所以,当默认使用的时候,opencv运行优化过的代码,否则运行没有优化过的代码。你可以使用cv2.useOptimized()函数来检查它是否已经启用,也可以使用cv2.setUseOptimized()来开启或者关闭优化。
样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# check if optimization is enabled
In [5]: cv2.useOptimized()
Out[5]: True

In [6]: %timeit res = cv2.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop

# Disable it
In [7]: cv2.setUseOptimized(False)

In [8]: cv2.useOptimized()
Out[8]: False

In [9]: %timeit res = cv2.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop

3.3 IPython中的检测性能

首先,IPython是什么:
它只是python中的一个模块,可以通过pip来安装。
然后,它有什么用处:
是python的交互式shell,比默认的python shell好用的多,支持自动缩进,bash shell命令,也内置了很多有用的函数以及功能,可以高效使用python,也是利用python来进行科学计算和交互可视化的平台。

主要功能:
1.运行ipython控制台
2.使用ipython作为系统shell
3.使用历史输入(history)
4.Tab补全
5.使用%run命令运行脚本
6.使用%timeit命令快速测量时间
7.使用%pdb命令快速debug
8.使用pylab进行交互计算
9.使用IPython Notebook

但是,我居然还没用过。。。我直接找一些资料先补充。以后用到再说。
样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [10]: x = 5

In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop

In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop

In [15]: z = np.uint8([5])

In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop

In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop

说实话,看到上面的样例,感觉有点像Linux中的time函数,我偶尔发现的。
再次提醒,python的数值计算远快于numpy数值计算。所以仅包含一两个元素的运算,我们尽量使用python的数值运算,当数组的大小更大的时候使用numpy。

1
2
3
4
5
In [35]: %timeit z = cv2.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop

In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop

所以opencv函数比numpy函数快25倍,说实话,这是怎么算出来的?370/15.8,恩就这样。

3.4 更多IPython的指令

还有其他命令来测量性能,分析性能,行分析,存储空间测量等。

3.5 优化性能的技巧

注意,首先简单实现算法,之后分析,找到瓶颈对其优化。
1.尽量不要在python中使用循环,尤其是多重循环
2.numpy和opencv已经对向量运算进行了优化,因此尽量使用向量
3.利用缓存?
4.除非必要,不要复制数组,而是尝试使用视图。??
如果这样依旧无法解决问题,那么使用Cython库。

4.opencv中的数学运算工具

这里面很复杂,包含SVD和PCA。
SVD Singular Value Decomposition奇异指分解 数值降维
PCA Principal Component Analysis主成分分析 降维算法
这两个方法我会专门进行补充。。待定

Ubuntu上opencv教程2之用户界面

0.简介

这部分分为多个部分:
1.图片入门
2.视频入门
3.绘画功能
4.将鼠标作为画笔
5.跟踪条作为调色板

1.图片入门

1.1 读取图片

使用cv2.imread()函数来获取图片,图片需要在工作目录或者完整的目录中。

第二个变量是图片读取方式的标识。
1.cv2.IMREAD_COLOR
加载一个彩色的图片,任何的图片的透明度都会被忽视,默认选项。
2.cv2.IMREAD_GRAYSCALE
加载一个灰度图片
3.cv2.IMREAD_UNCHANGED
加载图片包含alpha通道(不知道)

可以直接用1,0,-1来分别表示。

样例:

1
2
3
4
5
import numpy as np
import cv2

#load an color image in grayscale
img = cv2.imread('img.jpg',0)

1.2 显示图片

使用cv2.imshow()函数在一个窗口中显示图片,窗口自动适应图片的大小。

第一个参数是窗口的名字,是一个字符串。
第二个参数是图片。
你可以创造任意多的窗口,每个窗口有不同的名字。

1
2
3
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.waitKey()是一个键盘绑定功能。他的变量是时间,单位是毫秒。
这个函数等待特定的时间,来等待任意键盘事件。如果你按下按键,程序继续执行。

当变量是0,它一直等待按键按下,这也可以被设置为等待特定的按键。

cv2.destroyAllWindows()是关闭我们建立的所有窗口。如果你想关闭特定的窗口,可以使用函数cv2.destroyWindow(),变量为具体的窗口名称。

1.3 保存图片

cv2.imwrite()来保存图片
cv2.imwrite(‘img.png’,img)
将图片以PNG方式保存在工作路径下。

1.4 小结

将图片以灰度图的形式保存,然后展示,并且当输入s的时候保存,当输入ESC的时候,退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import cv2

img_path = "./pc1.png"

img = cv2.imread(img_path,0)
cv2.imshow("pic",img)

k = cv2.waitKey(0)

if k == 27:
cv2.destroyAllWindows()
elif k == ord('s')
cv2.imwrite('backup.png',img)

注意,当使用64位机器,你需要将
k = cv2.waitKey(0)
修改为
k = cv2.waitKey(0) & 0xFF

1.5 使用Matplotlib

Matplotlib是一个Python的绘制库,包含了大量的绘制函数,可以使用Matplotlib来显示图片,选择图片区域以及保存它。

更多的待定补充

2.视频入门

2.1 捕获视频和播放视频

捕获视频需要创造VideoCapture对象,变量可以是设备目录或者视频文件的名字,设备目录只是指定摄像头的数字,之后就会一帧一帧地获取视频,最后不要忘记释放捕获。

cap.read()返回一个bool值,如果帧被正确读取,返回1,所以可以通过检测返回值来检查是否视频结束。

有时候,cap也许没有初始化摄像头,会显示错误,可以通过cap.isOpened()来判断是否初始化完成,如果返回1,继续,否则,使用cap.open()函数。

你可以通过cap.get(propld)来获取视频的参数,使用cap.set(propld,value)来设置视频参数的值。
例如,可以通过cap.get(3)和cap.get(4)检查帧的宽度和长度,默认宽长度是640×480。
修改为320×240,需要

1
2
ret = cap.set(3,320)
ret = cap.set(4,240)

如果你遇到错误,确保摄像头可以正常工作通过使用其他的摄像头程序,例如cheese。

样例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import cv2

cap = cv2.VideoCapture(0)
/*
上面的是读取摄像头,如果读取视频文件那么就用下面的方法
cap = cv2.VideoCapture('vtest.avi')
*/
while(True):
ret,frame = cap.read()
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
cv2.imshow('frame',gray)
if cv2.waitKey(1) & 0xFF == ord('q')
break
cap.release()
cv2.destroyAllWindows()

注意里面cv2.waitKey()里面的参数,如果参数太小,视频播放就会非常快速,大的话就会播放速度很慢。25微秒是合适的。

确保安装了合适版本的ffmpeg或者gstreamer,有时候版本不对会导致错误。

2.2 保存视频

我们捕获一个视频,一帧一帧地播放视频然后保存视频。
保存图片只需要cv2.imwrite(),保存视频就稍微有些复杂。

当我们创建了一个VideoWriter对象,我们应该确定输出文件的名称,然后我们应该确定FourCC代码,这个是什么,待定。然后fps每秒的帧数和帧的大小应该确定,最后是isColor标志,来确定是彩色还是灰度。

FourCC是4字节的代码用来确定视频的解码器,我们可以使用下列的解码器In Fedora: DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID is more preferable. MJPG results in high size video. X264 gives very small size video)
In Windows: DIVX (More to be tested and added)
In OSX : (I don’t have access to OSX. Can some one fill this?)

上面的待定,自己看

1
2
3
4
import cv2

cap = cv2.VideoCapture(0)

2.3 绘制函数

常用变量:
img 绘画图形所在的图片
color 图片的形状 BGR 例如(255,0,0)
thickness 厚度,如果为-1,那么全部填充
lineType 线条的格式, 是否8联,抗混叠等。

2.3.1 画线

图片,起点,终点,颜色,厚度

1
img = cv2.line(img,(0,0),(511,511),(250,0,0),5)

2.3.2 画矩形

图片,左上角,右下角,颜色,厚度

1
img = cv2.rectangle(img,(384,0),(510,128),(0,255,0),3)

2.3.3 画圆

图片,圆心,半径,颜色,厚度

1
img = cv2.circle(img,(447,63), 63, (0,0,255), -1)

2.3.4 画椭圆

图片,中心位置,xy轴长度,旋转角度逆时针方向,开始角度,结束角度从长轴顺时针方向,颜色,厚度

1
img = cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1)

2.3.5 画多边形

1
2
3
pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
pts = pts.reshape((-1,1,2))
img = cv2.polylines(img,[pts],True,(0,255,255))

2.3.6 文字

1
2
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2,cv2.LINE_AA)

其中,变量有图片,字符串,位置,字格式,字大小,颜色,厚度,线风格等等

2.4 鼠标作为画笔

学习关于鼠标事件cv2.setMouseCallback()

1
2
3
>>> import cv2
>>> events = [i for i in dir(cv2) if 'EVENT' in i]
>>> print(events)

cv2.EVENT_MOUSEMOVE 鼠标滑动
cv2.EVENT_LBUTTONDOWN 左键点击
cv2.EVENT_RBUTTONDOWN 右键点击
cv2.EVENT_MBUTTONDOWN 中间点击
cv2.EVENT_LBUTTONUP 左键释放
cv2.EVENT_RBUTTONUP  右键释放
cv2.EVENT_MBUTTONUP 中间释放
cv2.EVENT_LBUTTONDBLCLK 左键双击
cv2.EVENT_RBUTTONDBLCLK 右键双击
cv2.EVENT_MBUTTONDBLCLK 中间双击,
以及FLAG:
cv2.EVENT_FLAG_LBUTTON 左键拖拽  
cv2.EVENT_FLAG_RBUTTON 右键拖拽

下面需要cv2.setMouseCallback函数,这个非常特别,里面的参数。
而且,可以先创造窗口,然后画图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cv2
import numpy as np

# mouse callback function
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDBLCLK:
cv2.circle(img,(x,y),100,(255,0,0),-1)

# Create a black image, a window and bind the function to window
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)

while(1):
cv2.imshow('image',img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()

2.5 轨迹栏作为调色板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import cv2
import numpy as np

def nothing(x):
pass

# Create a black image, a window
img = np.zeros((300,512,3), np.uint8)
cv2.namedWindow('image')

# create trackbars for color change
cv2.createTrackbar('R','image',0,255,nothing)
cv2.createTrackbar('G','image',0,255,nothing)
cv2.createTrackbar('B','image',0,255,nothing)

# create switch for ON/OFF functionality
switch = '0 : OFF \n1 : ON'
cv2.createTrackbar(switch, 'image',0,1,nothing)

while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break

# get current positions of four trackbars
r = cv2.getTrackbarPos('R','image')
g = cv2.getTrackbarPos('G','image')
b = cv2.getTrackbarPos('B','image')
s = cv2.getTrackbarPos(switch,'image')

if s == 0:
img[:] = 0
else:
img[:] = [b,g,r]

cv2.destroyAllWindows()

Ubuntu上opencv教程1之简介

0.总结

这个部分的内容在于如何在电脑中配置opencv-python,以及介绍。

1.介绍

1.1 opencv

opencv项目1999年开始,2000年公布。
现在opencv支持很多语言C++,Python,Java等等。可以在很多平台上使用Windows,Linux,OS X,Android,IOS等等。

1.2 opencv-python

opencv-python是opencv的python的API接口。它结合了opencv的C++API接口和Python语言。

因为python的简单和可读性,而十分流行。

和其他语言C/C++相比,Python速度较慢,但是,Python可以容易地用C/C++来扩展。这个特性有助于在C/C++中编写计算密集型代码,并且为其创建Python包装器,然后可以将这些包装器来用作Python模块。因此,这个有两个优点:

1.它的后台实际是C++代码,所以我们的代码和原始C/C++代码一样快。

2.Python中代码非常容易

这就是opencv python的工作原理,它就是围绕原始C++实现的Python包装器。

Numpy的支持使任务变得非常容易,Numpy是一个高度优化的数值运算库。提供了一个MATLAB风格的语法。所有OpenCV数组结构都转变为Numpy数组。

opencv python教程
开始之前需要了解python和numpy,因为用opencv python编写优化代码,就必须具备良好的numpy知识。

2.ubuntu上opencv使用入门

2.1 Ubuntu上使用摄像头

1
ls /dev/v*  //  可以看到摄像头,例如/dev/video0,表示成功驱动摄像头    

2.2如何安装应用程序显示摄像头捕捉到的视频

1
2
sudo apt install camorama
sudo apt install cheese //camorama/cheese分别是两款应用程序

2.3opencv操作方式

使用opencv需要使用pip来下载相关的包,但是下载源在国外,将下载源修改到国内就可以加速下载了。

1
2
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名
//这个是使用清华的镜像源

上面是临时的方法,下面是永久的方法:

1
2
3
4
5
6
7
mkdir ~/.pip
vim ~/.pip/pip.conf

[global]
index-url = http://mirrors.aliyun.com/pypi/simple
[install]
trusted-host = mirrors.aliyun.com

为了使用opencv需要使用python3中的包

1
pip3 install opencv-python

之后需要相关的包也这样下载

1
2
3
4
5
6
7
8
9
10
11
12
import cv2
path = "..."
image = cv2.imread(paht) // 输入代操作的图片的路径
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)// 灰度转换,降低计算强度
cv2.rectangle(image,(x,y),(x+w,y+w),(0,255,0),2)
//对图片进行编辑,最后一个参数为画笔的大小
cv2.imshow("image-title",image) // 显示图像
cv2.waitKey(0)
//注意要加上这一句要不然会看不见图片,
//参数为延迟多少毫秒,当为0时,延迟无穷大秒
face_cascade = cv2.CascadeClassifier(...)
//获取人脸识别的训练数据,来自Github上opencv/data/haarcascades

2.4人脸识别源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import cv2 

image_path = "./pc1.png"
facedata_path = "./haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(facedata_path)

image = cv2.imread(image_path)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow("Picture",image)
cv2.waitKey(0)
faces = face_cascade.detectMultiScale(
gray,
scaleFactor = 1.15,
minNeighbors= 5,
minSize = (5,5),
flags = cv2.CASCADE_SCALE_IMAGE
)

print('hello')
print("find {0} face(s)!",format(len(faces)))

for(x,y,w,h) in faces:
cv2.circle(image,((x+x+w)/2,(y+y+h)/2),w/2,(0,255,0),2)
cv2.imshow("Find Faces!",image)
cv2.waitKey(0)

Ubuntu上opencv教程0之入门

这一部分教程分为多个部分:
1.opencv的简介(已完成)
学习如何在电脑上设置opencv-python

2.opencv上Gui特性(已完成)
了解如何显示和保存图像和视频,控制鼠标事件和创建轨迹栏

3.核心操作(已完成) 学习基础的图片操作,像像素编辑,几何变换,代码优化,一些数学工具等。

4.opencv图片操作(ing)
学习opencv中不同的图片处理功能。

5.特征检测和描述
学习特征检测和描述

6.视频分析
学习不同的技术来处理视频,如对象跟踪等。

7.摄像机标定和三位重建
学习摄像定位和立体成像等。

8.机器学习
学习机器学习

9.计算摄影
学习不同的计算摄影技术,如图像去噪等。

10.目标检测
学习目标检测技术,如人脸检测等。

11.opencv-python绑定
如何生成opencv-python绑定。

比特币分析1之比特币的由来及原理

0.简介:

这篇文章主要讲解其大概的原理,但是对其数据结构,算法和协议没有分析。

1.比特币的诞生

这一部分很少,主要将了比特币的诞生的原因,里面包含了货币的发展过程。

1.1 以物易物

一开始,生产力较低,生活靠自给自足,没有大规模的贸易,相互之间的交易通过以物易物来实现。

1.2 实体货币

将黄金等贵重金属作为一般等价物,将所需要的物品通过一般等价物为基础,进行交换,从而避免了以物易物。使用货币来得到需要的货物。

1.3 符号货币

重金属货币容易磨损,不易携带,开采困难等问题,导致了钞票的出现,国家发行钞票,将重金属货币回收,以钞票作为抵押,承诺钞票和金属货币相同。

1.4 中央系统虚拟货币

为了减少货币发行的数量,将纸币回收,在帐目上记下回收的纸币的数量,当交易时,直接修改帐目,支付的过程变为了账本上数字的变更。

1.5 分布式虚拟货币

中央系统完全依赖于账本持有人的信用,如果账本的数据随意篡改,货币系统就会崩溃。
为了不以来任何中央处理,设置了分布式虚拟货币。

2. 比特币系统框架

2.1 账本公开

账本上不再记载人的余额,而是记载每一笔交易,付款人,收款人和付款金额。只要账本的初始状态确定,每一笔交易记录可靠并且有时序,当前每个人有多少钱是可以推算出来的。

账本公开,只要任何人需要都可以获取当前完整的账本,账本上记录了从账本创建开始到当前所有的交易记录。

2.2 身份和签名机制(公钥加密系统)

每个人的身份是保密的,有一个保密印章和印章扫描器。

保密印章隐含整个比特币网络中唯一的一串字符串,但字符串被隐藏了。无法被伪造。

印章扫描器可以扫描已经盖好的章,读出隐含信息,显示字符。

2.3 成立挖矿群体

矿工为组为单位,一组可以是单独的一个人,也可以是几个人。
矿工从事挖矿活动。
挖矿可以随时退出。
矿工有一定可能性获得报酬。

2.4 创建初始账本(创世块)

账本的开始记录了交易记录,付款人是系统,一开始比特币很少,随着比特币的流通和矿工的活动,比特币会增加。

3. 比特币工作流程

下面是在比特币的系统中是如何实现挖矿和交易的。

3.1 交易

A给B10个比特币,那么A,B分别有一个标识字符串a,b。那么交易单上有:a给b10个比特币,来源是系统送的。后面是a的保密章。

B拿到交易单之后,用印章扫描器来确定是a签署的,但是还需要矿工来确定付款人是否有足够的余额,记账和确认交易的有效性。

3.2 挖矿

挖矿是最为复杂的部分,但是很贴切。

每个组都复制一份初始账本,

Ubuntu上STM32开发之链接文件分析

0 简介

接着上面的启动文件的分析之后,这一篇就是关于链接文件的介绍,链接文件也就是:stm32_flash.ld。连接器需要通过链接文件来形成最后的目标文件。
链接程序脚本是提供给链接程序的脚本,用于指定内存布局并在执行固件时初始化固件所使用的各个内存段。这些脚本至关重要,因为它们指定了闪存和RAM的起始地址以及它们的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* Entry Point */
ENTRY(Reset_Handler)
/*
指定程序的入口点,即程序中要执行的第一条指令。
*/
/* Highest address of the user mode stack */
_estack = 0x20005000; /* end of 20K RAM */
/*
声明一个变量_estack,该值表示堆栈的开始。
*/

/*
HEAP 和 STACK 在前面的启动文件中有解释
*/
_Min_Heap_Size = 0; /* required amount of heap */
_Min_Stack_Size = 0x100; /* required amount of stack */

/*
这一部分需要根据你选择的芯片来确定。
在我之前的关于STM32下载的博客中,通过stm32flash可以看到
Interface serial_posix: 57600 8E1
Version : 0x22
Option 1 : 0x00
Option 2 : 0x00
Device ID : 0x0410 (STM32F10xxx Medium-density)
- RAM : 20KiB (512b reserved by bootloader)
- Flash : 128KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB
在下载程序的时候可以看到是下载到flash里面,下载地址从0x08000000开始。
ORIGIN 起始地址
LENGTH 大小
*/
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
}

/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH

/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc. ) */
*(.rodata*) /* .rodata* sections (constants, strings, etc. ) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */

KEEP (*(.init))
KEEP (*(.fini))

. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH

.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH

.ARM.attributes : { *(.ARM.attributes) } > FLASH

.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH

/* used by the startup to initialize data */
_sidata = .;

/* Initialized data sections goes into RAM, load LMA copy after code */
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */

. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM

/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)

. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM

PROVIDE ( end = _ebss );
PROVIDE ( _end = _ebss );

/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(4);
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} >RAM

/* MEMORY_bank1 section, code must be located here explicitly */
/* Example: extern int foo(void) __attribute__ ((section (". mb1text"))); */
.memory_b1_text :
{
*(.mb1text) /* .mb1text sections (code) */
*(.mb1text*) /* .mb1text* sections (code) */
*(.mb1rodata) /* read-only data (constants) */
*(.mb1rodata*)
} >MEMORY_B1

/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}

在看完上面的链接文件,以及之前博客关于启动文件的分析,可以了解到。

在文件的一开始,便将程序的入口地址设定为了Reset中断。

进入Reset中断之后,首先将从0x80000(这里有几个0我数不清了,随便打的)开始的flash地址中的data数据复制到RAM中,其中flash中的数据就是我们下载的工程的程序,然后将bss段清零初始化。

重新回顾一下text.data数据段和bss数据段:
TEXT:
通常是指用来存放程序执行带没的一块内存,这部分区域的内容在程序运行之前就已经确定了,通常属于只读,但是某些架构也会允许读写,也就是修改程序。里面可能包含一些常数变量。有的地方写为CODE。

DATA(data segment):
通常是指用来存放程序中已经初始化的全局变量的一块内存,属于静态分配。

BSS(bss segment):Block Started by Symbol 通常指用来存放程序中没有初始化的全局变量的一块内存,属于静态内存分配。

但是,为什么需要进行复制呢?考虑一下flash和RAM的关系。
都是随机存储器,断电数据消失,但Flash有点不一样,它在消失数据之前,添加了一个“性质”,这个性质能上电后再识别,且把这个信号返回到ram中,这样近似的把flash当成了eeprom来使用,就是这样,RAM芯片断电后数据会丢失,Flash芯片断电后数据不会丢失,但是RAM的读取数据速度远远快于Flash芯片。

所以,将flash中的data数据复制到ram中大概可以提高运行速度之类的。我猜的。有必要看一下哪些数据被分配到了data数据段。

接着进行跳转,先是系统初始化,也就是调用系统时钟初始化函数。
然后调用静态构造函数,对静态数据进行初始化的函数,从名字上来看。
接着调用自己编写的工程,从main函数开始。

Ubuntu上STM32开发之文件分析

0 简介

在建立stm32f1的工程之后,通过makefile来管理所有工程,然后通过gcc-arm-none-eabi工具链编译链接,通过串口stm32flash下载,接着用minicom来进行串口通信之后,终于完成了初步的了解。

那么,这次我主要想了解一下,stm32f1工程中调用的ST官网下载的固件库的文件,里面包含了启动,链接,库函数文件的意义。

至于这个工程,可以找我的仓库。

1 启动文件startup_stm32f10x_md.s

这个启动文件是适合gnu编译器的启动文件,记得,之前说过,不同的编译器,需要选择不同的启动文件。
看后缀就知道这个是汇编语言的文件,但是这是ARMCPU中的汇编和之前学过的x86CPU中的汇编不太一样可以参考GNU ARM Assembler Quick Reference文件来查询。

启动文件主要的定义了:

  1. 初始SP
    //_estack变量就是SP(stack point),由连接文件定义
  2. 初始PC = Reset_Handler 复位中断
  3. 设置中断向量表的入口和 ISR地址
    //ISR就是中断服务处理 Interrupt Service Routines
  4. 配置了系统时钟
  5. 分支到了C库的main函数,最终调用main函数

复位后,Cortex-M3处理器进入到线程模式,优先级为Privileged,堆栈设置在了main。
//这一句不了解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
.syntax unified     //说明下面的指令是ARM和THUMB通用格式
.cpu cortex-m3 //定义内核为Cortex-M3
.fpu softvfp //定义fpu使用
.thumb //定义汇编代码为Thumb指令集

//定义全局标签可以对外部链接程序可见,
.global g_pfnVectors //该标签指向中断向量表
.global Default_Handler //该标签指向默认中断,为死循环

/*
通常情况下:
BSS(bss segment):Block Started by Symbol 通常指用来存放程序中没有初始化的全局变量的一块内存,属于静态内存分配

DATA(data segment):
通常是指用来存放程序中已经初始化的全局变量的一块内存,属于静态分配。

TEXT(sode segment):
通常是指用来存放程序执行带没的一块内存,这部分区域的打掉在程序运行之前就已经确定了,通常属于只读,但是某些架构也会允许读写,也就是修改程序。里面可能包含一些常数变量。有的地方写为CODE。

上面的三个在GNU中可以默认识别。下面程序中还有相关的介绍。

HEAP:
用于存放进程运行中被动态分配的内存段,大小不固定,可以动态增减,当进程调用malloc等函数分配内存时,新分配的内存会添加到堆上,当调用free等函数释放内存时,就会从堆中剔除。

STACK:
栈,有称之为堆栈,是用户存放程序临时创建的局部变量,也就是函数{}中定义的变量,但是不包括static申明的变量,static申明的变量在数据段中存储。同时,函数被调用时候,参数也会进入发起调用的进程栈中,调用结束之后,函数的返回值,也会存放在栈中,因为先进先出的特点,特别方便用来保存,回复调用现场。可以将它看作一个寄存,交换临时数据的内存区。
*/

/*label: .word value 将4字节的value放在由连接器分配的label地址上 */
.word _sidata //.data部分 初始化的数值 启动地址
.word _sdata //.data部分 在链接脚本定义的 启动地址
.word _edata //.data部分 在链接脚本定义的 结束地址
.word _sbss //.bss部分 在链接脚本定义的 启动地址
.word _ebss //.bss部分 在链接脚本定义的 结束地址

/*.equ <symbol name>, <value>直接对标签赋值类(似于armasm中的EQU)*/
.equ BootRAM, 0xF108F85F

/*
这是处理器在复位后首次开始执行时调用的代码,仅执行必要的设置,然后调用应用程序提供的main函数。
.section <section_name> {,”<flags>”}
开始一个新的代码或者数据区域。 之前说过的三个区域有默认的标志,并且三个区域名称是连接器所默认的
下面是一些区域的标志指示
<Flag> Meaning
a allowable section
w writable section
x executable section
*/

.section .text.Reset_Handler
/*.weak是弱申明,为了将来可以重映射之类的,和printf重映射类似,将来可以重新定义函数的内容*/
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler: //将Reset_Handler复位中断放在程序的开始确定CP

/*下面这部分的含义是将flash中的数据段复制到SRAM中 */
movs r1, #0 //将立即数0赋值给r1寄存器
/*arm CPU中的b指令和x86 CPU中的jmp指令类似。branch,无条件分支*/
b LoopCopyDataInit //程序转移到LoopCopyDataInit处

CopyDataInit:
ldr r3, =_sidata //从存储器中将_sidata加载到寄存器r3中
ldr r3, [r3, r1] //从地址r3+r1处读取一个字(32bit)到r3中 r3为基地址,r1为偏移地址
str r3, [r0, r1] //把寄存器r3的值存储到存储器中地址为r0+r1地址处
adds r1, r1, #4 //r1+4

LoopCopyDataInit: //循环拷贝数据
/*
LDR (immediate offset)
Load with immediate offset, pre-indexed immediate offset, or post-indexed immediate offset.
*/
ldr r0, =_sdata //DATA起始地址
ldr r3, =_edata //r3给出尾地址
/*
adds,如果算术指令的最后有s,就代表n,z,c,v符号位也会立即更新
n == 0 (Negative)结果不是负数
z == 1 (Zero)结果是0
c == 1 (Carry)有进位
v == 0 (Overflow)溢出
*/
adds r2, r0, r1 //r2=r0+r1
/*cmp r1,r2 :r1-r2,然后根据运算结果更新标志位*/
cmp r2, r3 //r2和r3比较,地址还在data段
/*Branch if C is Clear当没有借位的时候跳转到下面*/
bcc CopyDataInit
ldr r2, =_sbss //从存储器中将_sbss加载到寄存器r2中
b LoopFillZerobss//循环置位bss段
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4

LoopFillZerobss:
ldr r3, = _ebss //从存储器中将_ebss加载到寄存器r3中
cmp r2, r3 //同上
bcc FillZerobss
/*
bl 有返回的跳转,
在跳转到子程序之前,将下一条指令的地址拷贝到LR链接寄存器(R14)
由于BL指令保存了下条指令的地址,
所以可以使用MOV PC LR来实现子程序的返回。
*/
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
/*
bc lr类似于
mov pc ,lr
跳转到lr(链接寄存器)也就是返回地址
*/
bx lr
.size Reset_Handler, .-Reset_Handler

.section .text.Default_Handler,"ax",%progbits
Default_Handler://这就是上面说的默认中断,是一个死循环
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors://这就是上面说的中断向量表
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler
.word PVD_IRQHandler
.word TAMPER_IRQHandler
.word RTC_IRQHandler
.word FLASH_IRQHandler
.word RCC_IRQHandler
.word EXTI0_IRQHandler
.word EXTI1_IRQHandler
.word EXTI2_IRQHandler
.word EXTI3_IRQHandler
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.word DMA1_Channel2_IRQHandler
.word DMA1_Channel3_IRQHandler
.word DMA1_Channel4_IRQHandler
.word DMA1_Channel5_IRQHandler
.word DMA1_Channel6_IRQHandler
.word DMA1_Channel7_IRQHandler
.word ADC1_2_IRQHandler
.word USB_HP_CAN1_TX_IRQHandler
.word USB_LP_CAN1_RX0_IRQHandler
.word CAN1_RX1_IRQHandler
.word CAN1_SCE_IRQHandler
.word EXTI9_5_IRQHandler
.word TIM1_BRK_IRQHandler
.word TIM1_UP_IRQHandler
.word TIM1_TRG_COM_IRQHandler
.word TIM1_CC_IRQHandler
.word TIM2_IRQHandler
.word TIM3_IRQHandler
.word TIM4_IRQHandler
.word I2C1_EV_IRQHandler
.word I2C1_ER_IRQHandler
.word I2C2_EV_IRQHandler
.word I2C2_ER_IRQHandler
.word SPI1_IRQHandler
.word SPI2_IRQHandler
.word USART1_IRQHandler
.word USART2_IRQHandler
.word USART3_IRQHandler
.word EXTI15_10_IRQHandler
.word RTCAlarm_IRQHandler
.word USBWakeUp_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word BootRAM //0x108. This is for boot in RAM mode
/*
下面的都是对中断函数进行弱申明
在工程中会在stm32f10x_it文件中重新申明
*/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler

.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler

.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler

.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler

.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler

.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler

.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler

.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler

.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler

.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler

.weak TAMPER_IRQHandler
.thumb_set TAMPER_IRQHandler,Default_Handler

.weak RTC_IRQHandler
.thumb_set RTC_IRQHandler,Default_Handler

.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler

.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler

.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler

.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler

.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler

.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler

.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler

.weak DMA1_Channel1_IRQHandler
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler

.weak DMA1_Channel2_IRQHandler
.thumb_set DMA1_Channel2_IRQHandler,Default_Handler
.weak DMA1_Channel3_IRQHandler
.thumb_set DMA1_Channel3_IRQHandler,Default_Handler

.weak DMA1_Channel4_IRQHandler
.thumb_set DMA1_Channel4_IRQHandler,Default_Handler

.weak DMA1_Channel5_IRQHandler
.thumb_set DMA1_Channel5_IRQHandler,Default_Handler

.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler

.weak DMA1_Channel7_IRQHandler
.thumb_set DMA1_Channel7_IRQHandler,Default_Handler

.weak ADC1_2_IRQHandler
.thumb_set ADC1_2_IRQHandler,Default_Handler

.weak USB_HP_CAN1_TX_IRQHandler
.thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler

.weak USB_LP_CAN1_RX0_IRQHandler
.thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler

.weak CAN1_RX1_IRQHandler
.thumb_set CAN1_RX1_IRQHandler,Default_Handler

.weak CAN1_SCE_IRQHandler
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler

.weak TIM1_BRK_IRQHandler
.thumb_set TIM1_BRK_IRQHandler,Default_Handler

.weak TIM1_UP_IRQHandler
.thumb_set TIM1_UP_IRQHandler,Default_Handler

.weak TIM1_TRG_COM_IRQHandler
.thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler

.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler

.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler

.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler

.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler

.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler

.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler

.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler

.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler

.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler

.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler

.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler

.weak RTCAlarm_IRQHandler
.thumb_set RTCAlarm_IRQHandler,Default_Handler

.weak USBWakeUp_IRQHandler
.thumb_set USBWakeUp_IRQHandler,Default_Handler

好了上面写的很复杂,主要是有一些必要的知识点补充,可以看原本没有注释的文件,就能够看懂这个启动文件的内容了。

下面是启动流程:
首先,STM32上电通过BOOT0和BOOT1两个引脚连接高低电平来确定启动初始地址在哪里,STM32启动位置有三种:

1.Main Flash memory
是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。

2.System memory
从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少。

3.Embedded Memory
内置SRAM,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。

Ubuntu上vim进阶教程

0 简介

这是对我之前的博客vim操作指南的一个补充,是对于我的配置的一些讲解

我有一个仓库,里面就是关于vim的配置。之前也提到过:https://github.com/Emir-Liu/vimrc

我们看一看user vimrc file,里面有下面的内容:

1
2
3
4
5
6
7
8
9
10
11
12
set runtimepath+=~/.vim_runtime

source ~/.vim_runtime/vimrcs/basic.vim
source ~/.vim_runtime/vimrcs/filetypes.vim
source ~/.vim_runtime/vimrcs/plugins_config.vim
source ~/.vim_runtime/vimrcs/extended.vim

try
source ~/.vim_runtime/my_configs.vim
catch
endtry

其中,采用了source命令,这是一个很有趣的东西,在当前的bash环境中,读取并且执行文件中的命令。

讲解一下source的来源,是从C Shell而来的,这是什么?是bash的内置命令。通常情况下,通常使用“.”来代替。

Ubuntu上STM32开发之UC/OS-III移植

0准备

makefile 管理工程脚本语言
STM32F1的固件库,之前的博客有提到
UC/OS III文件,从Micrium官网下载符合STM32的版本。

1提取必要的文件

UC/OS III文档中,该文件结构的分析:
EvalBoards:
开发板相关的文件,主要配置底层和系统,需要提取部分文件
uC-CPU:CPU相关的文件,但是我打算使用STM32F1的标准固件库,就不用这部分了
uC-LIB:这是Micrium官方的库,刚接触的时候可以不用
uCOS-III:这是关键的部分,主要的移植内容。其中有两个文件夹。
Ports:与系统相关的端口配置文件
Source:OS的全部文件

知道了文档的结构之后,该提取了。
提取uCOS-III文档,和EvalBoards中的app_cfg.h和os_cfg.h。
app_cfg.h:应用配置文件:任务优先级和堆栈大小等。
os_cfg.h:系统配置文件:使能相应的功能函数,裁剪系统
STM32固件库中的必要文件:
启动文件
链接文件
库文件

2 配置文件:

首先的我们需要配置系统节拍器system TIck TImer,SysTIck,也就是俗称的系统滴答,其作用类似于驱动整个系统的心脏。

2.1 初始化系统节拍器

我们使用ST的系统节拍器作为操作系统的系统节拍器,芯片的SysTick是内核Cortex-M3的一部分,所以,在初始化的时候调用CM3中的SysTick_Config函数。

说明一下,SysTick是专门为系统而设计的Cortex-M3内核的芯片的功能。

配置过程如下:

1
2
3
4
5
6
7
8
//app_cfg.c
void OSTick_Init(void)
{
RCC_ClocksTypeDef RCC_ClocksStructure;
RCC_GetClocksFreq(&RCC_ClocksStructure); //获取系统时钟频率
SysTick_Config(RCC_ClocksStructure.HCLK_Frequency/OS_TICKS_PER_SEC);
//初始化并启动SysTick和它的中断
}

2.2 系统节拍器中断配置

中断函数需要调用系统相关的函数

1
2
3
4
5
6
7
void SysTick_Handler(void)
{
OSIntEnter();
OSTimeTick();
OSIntExit();
//系统节拍器调用OS函数
}

2.3 裁剪系统

打开或者关闭系统的某些功能,也就是配置os_cfg.h文件。

2.3.1最低优先级 os_lowest_prio

优先级:优先级越小,数值越大。配置优先值数值的最大值。

2.3.2系统每秒的滴答数 os_ticks_per_sec

2.3.3任务堆栈大小 os_task_xxx_stk_size

2.4 配置os_cpu_a.asm文件

位于uc/os iii ports下面,主要是系统底层相关的一部分汇编代码,内容是对外部引用(全局变量,函数)声明,以及部分系统相关源代码(汇编)做定义。

3 工程结构

第一类 标准外设库
startup code:启动代码
STM32F10x_StdPeriph_Dricer:标准外设库驱动
CMSIS:标准接口

第二类 uC/OS III内核
uC/OS III Source:内核源代码
uC/OS III Ports:OS端口底层代码,与处理器,编译平台有关。

第三类 用户应用
Bsp:应用底层代码(初始化,驱动等)
App:应用实现代码(配置OS,应用等)

FAQ:
1.SYSTEM\sys\sys.c:33:7: error: expected ‘(‘ before ‘void’ __ASM void MSR_MSP(u32 addr)

我在编译工程的时候遇到的这个问题,说实话这个东西应该是编译器的版本的问题吧,大概,需要将上面的文件修改一些:

1
2
3
4
5
6
7
8
9
10
11
12
13
__ASM void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}

//将上面那种写法修改成下面这种应该就没事了
//为什么?我也不知道。总之先记下了。
void MSR_MSP(u32 addr)
{
__ASM volatile("MSR MSP, r0");
__ASM volatile("BX r14");
}