[转]一个 CSS3 的自定义滚动条

转载一个 css3 的自定义滚动条~

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
::-webkit-scrollbar {
height:11px;
width:11px
}
::-webkit-scrollbar-button {
height:0;
width:0
}
::-webkit-scrollbar-button:start:decrement,::-webkit-scrollbar-button:end:increment {
display:block
}
::-webkit-scrollbar-button:vertical:start:increment,::-webkit-scrollbar-button:vertical:end:decrement {
display:none
}
::-webkit-scrollbar-track:vertical,::-webkit-scrollbar-track:horizontal,::-webkit-scrollbar-thumb:vertical,::-webkit-scrollbar-thumb:horizontal,::-webkit-scrollbar-track:vertical,::-webkit-scrollbar-track:horizontal,::-webkit-scrollbar-thumb:vertical,::-webkit-scrollbar-thumb:horizontal {
border-style:solid;
border-color:transparent
}
::-webkit-scrollbar-track:vertical::-webkit-scrollbar-track:horizontal{
background-clip:padding-box;
background-color:#fff;
}
::-webkit-scrollbar-thumb {
-webkit-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);
background-clip:padding-box;
background-color:rgba(0,0,0,.2);
min-height:28px;
padding-top:100
}
::-webkit-scrollbar-thumb:hover {
-webkit-box-shadow:inset 1px 1px 1px rgba(0,0,0,.25);
background-color:rgba(0,0,0,.4)
}
::-webkit-scrollbar-thumb:active {
-webkit-box-shadow:inset 1px 1px 3px rgba(0,0,0,.35);
background-color:rgba(0,0,0,.5)
}
::-webkit-scrollbar-track:vertical,::-webkit-scrollbar-track:horizontal,::-webkit-scrollbar-thumb:vertical,::-webkit-scrollbar-thumb:horizontal {
border-width:0;
}
::-webkit-scrollbar-track:hover {
-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,.1);
background-color:rgba(0,0,0,.05)
}
::-webkit-scrollbar-track:active {
-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,.14),inset -1px -1px 0 rgba(0,0,0,.07);
background-color:rgba(0,0,0,.05)
}

ARM 20pin JTAG 和 SWD 定义

ARM 的引脚定义

二、标准 JTAG 20 PIN 信号定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. VCC                        3. VCC

3. TRST 4. GND

5. TDI 6. GND

7. TMS 8. GND

9. TCLK 10. GND

11. RTCK 12. GND

13. TDO 14. GND

15.RESET 16. GND

17. NC 18. GND

19.NC 20. GND

三、标准 SWD 20 PIN 信号定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. VCC                        3. VCC

3. NC 4. GND

5. NC 6. GND

7. SWDIO 8. GND

9. SWCLK 10. GND

11. NC 12. GND

13. SWO 14. GND

15. RESET 16. GND

17. NC 18. GND

19.NC 20. GND

值语义和对象语义

关于值语义和对象语义

说个在 c 艹中经常遇见的问题.

在 c++ 中和 c# 和 java 不很一样. 它允许程序员定义一个类的复制构造函数和复制赋值运算符, 这就表明对象是可以复制的. (这里的复制的意思是克隆一个完全一致, 但却是一个新的个体的对象)

这就允许程序员把对象当成值一样来使用, 例如:

1
2
3
4
string a = "hahaha";
string b = a;
int i = 100;
int j = i;

在这段代码中, string 和 int 使用起来没有差别, 令 j = i 确实是在内存上新申请了一个 int 的空间, 令他和 i 相等. 同理 string b 也是这样.(也就是复制之后和原对象脱离关系, 就像复制 int 一样)
虽然我知道 string 使用 class 来定义的, int 是 c 语言内置类型, 但是用起来却是完全一致.
另外, 你可以吧 string 放入 vector 中放入 map 中, 就和使用一个 int 一样简单.
所以 string 可以说是值语义的.

另外的一种用法就是在面向对象中比较常见的一种了. 就是用 class 定义一个具体的对象, 比如一个 TCP 连接, 一个文件对象.
我在前几天写程序时想要简单封装一个 tcp 连接的时候, 就陷入了一会遐想, 到底怎么定义复制构造函数和复制运算符. 答案是不要定义
因为复制一个对象在语义上本身就是不对的, 复制一个 tcp 连接是什么意思? 新打开一个连接?
所以说对于这种对象应该禁止他们的复制操作. 最好的方法是继承 boost::noncopyable.

那么, 不能复制了, 我想把 tcp 连接放入 vector 或者在函数中传递怎么办? 我是用的方法是统一使用 shared_ptr, 这样资源泄漏的问题也就解决了.
所以, 在实现一个资源相关的类时, 用 RAII 方法封装一下资源, 然后继承 noncopyable 应该能解决大部分问题.

用 thttpd+lua+cgi+jQuery mobile 搭建自动关机服务器

最近猎豹 wifi 共享精灵推出了一个 wifi 控制关机的功能. 我因为没有笔记本一直都用台式机 + linux nat + 无线 ap 来实现 wifi 共享 (见上次用虚拟机建立 NAT 曲线共享上网的经历忘了记录了). 所以看到这个功能之后一直眼红的不行, 在床上看视频到两三点再下去关电脑实在是破坏幸福感的事情. 所以嘛, 就又到了自己造轮子的时间.

首先分析一下需求, 很简单就是在手机上打开一个网页可以远程控制我的台式机关机.

所以必须要有一个 web 服务器, 另外一门 web 脚本语言也是必不可少的, 而且前端网页应该尽量漂亮, 并且适配手机.

首先 web 服务器应当尽量小巧, 一开始我用 shttpd 来做, 用了一天之后发现它对 http 验证支持的不很好. 所以便放弃他选择 thttpd 了. thttpd 也很小巧而且功能更多, 并且自称性能上不弱于 Apache.

然后脚本语言, 脚本语言的话我只会 lua 和 javascript, 如果用 javascript 的话需要用到 nodejs, 可是我有点不习惯 nodejs 的异步模型和一层一层的函数嵌套. 所以还是选 lua 吧. 而且 thttpd 支持 cgi, 可以和 lua 很好的适配

ok, 下面开始搭建环境. 因为对性能的要求不高, 所以决定在 Cygwin 下运行. 下载编译 thttpd, 直接 wget->./configure –prefix/usr->make->make install 就 ok 了.

中间可能在编译 htpasswd.c 文件时提示 getline 重定义. 直接把 htpasswd.c 文件里的所有 getline 改名成_getline 即可.

安装的时候会提示没有 / usr/man/man1 文件夹, 直接 mkdir /usr/man/man1 就 ok

thttpd 支持命令行启动同时也支持配置文件. 具体可以 man thttpd 来查看. 在源码包里的./contrib/redhat-rpm 文件夹有一个例子的配置文件. 我们把它改一下放到用户目录里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# This section overrides defaults
dir=/home/Administrator/webroot
#chroot
user=Administrator# default = nobody
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
# This section _documents_ defaults in effect
# port=80
# nosymlink# default = !chroot
# novhost
cgipat=/*.cgi
# nothrottles
# host=0.0.0.0
# charset=iso-8859-1

然后就是安装 lua 了, Cygwin 源里直接就有, 装上即可.

在用户目录里建立一个 webroot 文件夹, 网站放这里.

建立两个文件, 一个叫 index.cgi, 一个叫 shutdown.cgi 内容如下:

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
#!/bin/lua

local Z_SHUTDOWN_FILE = "/tmp/zshutdown"

print("Content-type: text/html")
print()

local is_shutting_down
local time_to_shutdown
local file = io.open(Z_SHUTDOWN_FILE, "r") --read模式, 可查看文件是否存在
if file == nil then
is_shutting_down = false

else
is_shutting_down = true
local t1 = assert(file:read("*n"))
time_to_shutdown = 30 - os.difftime(os.time(), t1)
if time_to_shutdown < 0 then
is_shutting_down = false
os.remove(Z_SHUTDOWN_FILE)
end
end

print[=[
<!Doctype html>
<html xmlns=https://www.w3.org/1999/xhtml>
<head>
<meta charset="utf-8">
<title>zzZ的自动关机</title>

<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<meta http-equiv="mobile-agent" content="format=html5;">

<link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css">
<script src="https://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>

</head>
<body>

<div data-role="page" id="main">

<div data-role="header" data-position="fixed">
<h1>zzZ的自动关机</h1>
</div>

<div style="text-align:center;" data-role="content">

]=]
if is_shutting_down then
print[=[
<p>您的电脑将在<span id="time" style="color:red">]=] print(time_to_shutdown); print[=[</span>秒后自动关机</p>
<script>
var t = ]=] print(time_to_shutdown); print[=[;
function decrease()
{ if (t > 0)$("#time").text(--t); }
setInterval("decrease()", 1000);
</script>
<p>按动按钮来取消关机:</p>
<a href="shutdown.cgi" data-rel="dialog" data-role="button" data-inline="true" data-icon="info">取消关机</a>
]=]
else
print[=[
<p>按动按钮来进行关机:</p>
<a href="#shutdown_confirm" data-rel="dialog" data-role="button" data-inline="true" data-icon="info">关机</a>
]=]
end
print[=[

</div>

<div data-role="footer" data-position="fixed">
<h1>Powered by zzZ</h1>
</div>

</div>

<div data-role="page" id="shutdown_confirm">
<div data-role="header">
<h1>真的要进行关机?</h1>
</div>

<div data-role="content">
<center>
<a href="shutdown.cgi" data-rel="dialog" data-role="button" data-inline="true" data-icon="info">确认</a>
<a href="#main" data-role="button" data-inline="true" data-icon="delete">取消</a>
</center>
</div>

<div data-role="footer">
<h1>Powered by zzZ</h1>
</div>

</div>

</body>
</html>
]=]
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
#!/bin/lua

local Z_SHUTDOWN_FILE = "/tmp/zshutdown"

print("Content-type: text/html")
print()

local is_shutting_down
local file = io.open(Z_SHUTDOWN_FILE, "r") --read模式, 可查看文件是否存在
if file == nil then
is_shutting_down = false
file = io.open(Z_SHUTDOWN_FILE, "w") --write模式, 打开文件并写入
file:write(os.time())
file:close()

os.execute('shutdown -s 30')
else
is_shutting_down = true
os.remove(Z_SHUTDOWN_FILE)

os.execute('shutdown -a')
end

print[=[
<!Doctype html>
<html xmlns=https://www.w3.org/1999/xhtml>
<head>
<meta charset="utf-8">
<title>关机命令已接受</title>

<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<meta http-equiv="mobile-agent" content="format=html5;">

<link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css">
<script src="https://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>

</head>
<body>

<div data-role="page" id="main">

<div data-role="header" data-position="fixed">
<h1>
]=]

if is_shutting_down then
print "关机已取消"
else
print "关机命令已接受"
end

print[=[
</h1>
</div>

<div data-role="content">
<center>
]=]

if is_shutting_down then
print[=[
<p>关机命令已取消</p>
<a href="javascript:location.reload(true)" data-role="button" data-inline="true" data-icon="info">返回</a>
]=]
else
print[=[
<p>您的电脑将在<span id="time" style="color:red">30</span>秒后自动关机</p>
<script>
var t = 30;
function decrease()
{ if (t > 0)$("#time").text(--t); }
setInterval("decrease()", 1000);
</script>
<a href="javascript:location.reload(true)" data-role="button" data-inline="true" data-icon="info">返回</a>
]=]
end

print[=[
</center>
</div>

<div data-role="footer" data-position="fixed">
<h1>Powered by zzZ</h1>
</div>

</div>

</body>
</html>

]=]

ok 了, 效果如图:
image_1bl0277mi94vhuh2v11d3gar49.png-219.7kB
image_1bl028649hrj10b9p66155e14jtm.png-153.8kB
image_1bl028n31qgj1ikq13e81ktcqam13.png-130kB
image_1bl029cp41lakb6l1f1m1tfmrf91g.png-136.7kB
image_1bl02a0ek97d1m54foe9suips1t.png-177.8kB
image_1bl02ajhhg2v1vqc1kmbmrm1ilh2a.png-124.4kB

为 openwrt 编译 htop

为 openwrt 编译 htop

其实就和原来给电视棒交叉编译一样, 不过这次交叉编译器可让我好找…

路由器用的是华为的 hg255d, mips 的芯片, 16M flash, 64M sdram.

一般嵌入式开发都把内核和 app 分开的… 这个 openwrt 却不单独提供 toolchain. 只给一个内核源码包 (也不只是内核, 是内核和软件包的混合体…)

不过最后还是找到一个单独的工具链:

下载地址:

有了工具链一切就好办了. htop 依赖与 ncurses 所以先下载 ncurses.

1
2
3
4
mkdir openwrt
cd openwrt
wget http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz
wget http://downloads.sourceforge.net/project/htop/htop/1.0.2/htop-1.0.2.tar.gz

下载好之后, 在当前目录建一个文件夹 build, 编译后的文件就安装在这里, 然后分步解压

1
2
3
mkdir build
tar zxvf ncurses-5.9.tar.gz
tar zxvf htop-1.0.2.tar.gz

然后先编译 ncurses:

1
2
3
4
cd ncurses-5.9
./configure --prefix=/home/zzz/openwrt/build --host=mipsel-openwrt-linux --without-cxx --without-cxx-binding --without-ada --without-manpages --without-progs --without-tests
make
make install

后面几个 without 是去除 c++ 和 ada 支持以及不编译 manpage 和测试程序
然后就能发现在 build/lib 里有编译好的 ncurses 库了

之后编译 htop:

1
2
3
4
cd htop-1.0.2
./configure --prefix=/home/zzz/openwrt/build --disable-unicode --host=mipsel-openwrt-linux LDFLAGS=-L/home/zzz/openwrt/build/lib
make
make install

LDFLAGS=-L/home/zzz/openwrt/build/lib 选项是为了加上链接库的路径

短暂等待过后, 在 build/bin 里应该能看见 htop.

拿到 openwrt 上运行又发生点问题. 提示
Error opening terminal: xterm.
上网 Google 一下发现需要设置 TERMINFO 环境变量:

1
export TERMINFO=/usr/share/terminfo

[EOF]

自己造操作系统 (2) – zOS-0.2 版本发布!

自己造操作系统系列之 0.2 版

新增了信号量功能, 实现进程间同步.

zOS 暂时分成两条线, 一条是上文说的那些功能 (类似于宏内核, 加载执行)

另一条是今天发布的 zOS-mini, 只包含调度, 同步设施. 而且只需要三个文件

还是只能在 stm32 上运行

链接: https://lengzzz.com/zOS/zOS_mini-0.2.zip

下面是一个测试用例
四个线程, 以不同的时间闪烁 led, 线程间通过 sem 信号量同步:

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
#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
#include "zOS.h"

void init();

#define BitP(addr, n) (*(volatile unsigned int *)(0x42000000 + (((unsigned)(addr) - 0x40000000) * 8 + (n)) * 4))
#define BitM(addr, n) (*(volatile unsigned int *)(0x22000000 + (((unsigned)(addr) - 0x20000000) * 8 + (n)) * 4))

#define STACK_SIZE 32

semaphore sem;

uint32_t stack0[STACK_SIZE];
void process0()
{
while (1)
{
BitP(&GPIOA->ODR, 4) = 1;
delay_ms(1000);
BitP(&GPIOA->ODR, 4) = 0;
delay_ms(1000);
}
}

uint32_t stack1[STACK_SIZE];
void process1()
{
while (1/*SYS_TICK < 10 * 1000 * 1000*/)
{
down_sem(&sem);
BitP(&GPIOA->ODR, 0) = 1;
delay_ms(125);
BitP(&GPIOA->ODR, 0) = 0;
delay_ms(125);
}
//create_process(&stack0[STACK_SIZE], process0);
//kill_process();
}
uint32_t stack2[STACK_SIZE];
void process2()
{
while (1)
{
down_sem(&sem);
BitP(&GPIOA->ODR, 1) = 1;
delay_ms(250);
BitP(&GPIOA->ODR, 1) = 0;
delay_ms(250);
}
}
uint32_t stack3[STACK_SIZE];
void process3()
{
while (1)
{
down_sem(&sem);
BitP(&GPIOA->ODR, 2) = 1;
delay_ms(500);
BitP(&GPIOA->ODR, 2) = 0;
delay_ms(500);
}
}
uint32_t stack4[STACK_SIZE];
void process4()
{
while (1)
{
up_sem(&sem);
up_sem(&sem);
BitP(&GPIOA->ODR, 3) = 1;
delay_ms(1000);
BitP(&GPIOA->ODR, 3) = 0;
delay_ms(1000);
}
}

int main(void)
{
init();
init_zos();

init_sem(&sem, 0);

create_process(&stack1[STACK_SIZE], process1);
create_process(&stack2[STACK_SIZE], process2);
create_process(&stack3[STACK_SIZE], process3);
create_process(&stack4[STACK_SIZE], process4);

schedule();

while (1);
}

void init()
{
GPIO_InitTypeDef gpio;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

gpio.GPIO_Pin =
GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);
}

Win7 开启路由转发功能

Win7 开启路由转发功能

原来以为 windows 只有 server 版本才有这个功能

今天发现 win7 也有

打开控制面板 -> 管理工具 -> 服务 ->Routing and Remote Access, 默认不开启, 开启就 ok 了

可以把两个网卡的网络连接起来
image_1bl043idv1j451elr1n8btk756i9.png-108.4kB

cortex-m3 系统异常相关寄存器

enc28j60 驱动和 udp 协议栈移植到 stm32 成功

1. 手动挂起、清除系统异常和中断挂起示意

软件挂起

类型 NMI PendSV SysTick
寄存器位 NMIPENDSET PENDSVSET PENDSTSET
软件清除
类型 PendSV SysTick
寄存器位 PENDSVCLR PENDSVCLR

中断挂起状态示意

ISRPENDING

寄存器: SCB_ICSR(Interrupt Control and state register)

image_1bl04ftuo1f9ck9318ol1pqn195c9.png-148.6kB
image_1bl04gc7heidbq71ikbn99tanm.png-174.2kB

2. 设置系统异常优先级

寄存器: SCB_SHPRx(System handler priority)

image_1bl04hde214oj1i9i13iv194p1vam13.png-101.3kB
image_1bl04hodm70q11la19pkuuh1jhr1g.png-116.3kB
image_1bl04i123oasa21c94f1u1r01t.png-57.5kB

3. 使能 fault,和挂起状态、活动状态示意

使能
类型 Usage fault Bus fault mem fault
寄存器位 USGFAULTENA BUSFAULTENA MEMFAULTENA
挂起状态
类型 SVC Bus fault mem fault Usage fault
寄存器位 SVCALLPENDED BUSFAULTPENDED MEMFAULTPENDED USGFAULTPENDED
活动状态
类型 SysTick PendSV Debug Monitor SVC Usage fault Bus fault Mem fault
寄存器位 SYSTICKACT PENDSVACT MONITORACT SVCALLACT USGFAULTACT BUSFAULTACT MEMFAULTACT

寄存器: SCB_SHCSR(System handler control and state register)

image_1bl04u1pi1o0v9cc19ugj11c672a.png-177.9kB

一个 dump 出 scb(System control block 系统控制块) 的函数, 调试的时候有时很有用

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
#define PRIORITY_Msk 0xF0
#define PRIORITY_Pos 4
void dump_scb()
{
uint32_t icsr = SCB->ICSR;
uint32_t shcsr = SCB->SHCSR;

printf("ICSR:\r\n");
printf("\t there %s interrupt pending\r\n",
(icsr & SCB_ICSR_ISRPENDING_Msk) ? "is" : "isn't");
printf("\t pending exception number:%u\r\n",
(icsr & SCB_ICSR_VECTPENDING_Msk) >> SCB_ICSR_VECTPENDING_Pos);
printf("\t active exception number:%u\r\n",
(icsr & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos);

printf("SHPRx:\r\n");
printf("\t Mem Manage:%hhd\r\n", (SCB->SHP[0] & PRIORITY_Msk) >> PRIORITY_Pos);
printf("\t Bus Fault:%hhd\r\n", (SCB->SHP[1] & PRIORITY_Msk) >> PRIORITY_Pos);
printf("\t Usage Fault:%hhd\r\n", (SCB->SHP[2] & PRIORITY_Msk) >> PRIORITY_Pos);

printf("\t SVC:%hhd\r\n", (SCB->SHP[7] & PRIORITY_Msk) >> PRIORITY_Pos);
printf("\t Debug Monitor:%hhd\r\n", (SCB->SHP[8] & PRIORITY_Msk) >> PRIORITY_Pos);

printf("\t PendSV:%hhd\r\n", (SCB->SHP[10] & PRIORITY_Msk) >> PRIORITY_Pos);
printf("\t SysTick:%hhd\r\n", (SCB->SHP[11] & PRIORITY_Msk) >> PRIORITY_Pos);

printf("SHCSR:\r\n");
printf("\t Usage Fault enable:%u\r\n",
(shcsr & SCB_SHCSR_USGFAULTENA_Msk) >> SCB_SHCSR_USGFAULTENA_Pos);
printf("\t Bus Fault enable:%u\r\n",
(shcsr & SCB_SHCSR_BUSFAULTENA_Msk) >> SCB_SHCSR_BUSFAULTENA_Pos);
printf("\t Mem Fault enable:%u\r\n",
(shcsr & SCB_SHCSR_MEMFAULTENA_Msk) >> SCB_SHCSR_MEMFAULTENA_Pos);

printf("\r\n");

printf("\t SVCall pended:%u\r\n",
(shcsr & SCB_SHCSR_SVCALLPENDED_Msk) >> SCB_SHCSR_SVCALLPENDED_Pos);
printf("\t Bus Fault pended:%u\r\n",
(shcsr & SCB_SHCSR_BUSFAULTPENDED_Msk) >> SCB_SHCSR_BUSFAULTPENDED_Pos);
printf("\t Mem Fault pended:%u\r\n",
(shcsr & SCB_SHCSR_MEMFAULTPENDED_Msk) >> SCB_SHCSR_MEMFAULTPENDED_Pos);
printf("\t Usage Fault pended:%u\r\n",
(shcsr & SCB_SHCSR_USGFAULTPENDED_Msk) >> SCB_SHCSR_USGFAULTPENDED_Pos);

printf("\r\n");

printf("\t SysTick active:%u\r\n",
(shcsr & SCB_SHCSR_SYSTICKACT_Msk) >> SCB_SHCSR_SYSTICKACT_Pos);
printf("\t PendSV active:%u\r\n",
(shcsr & SCB_SHCSR_PENDSVACT_Msk) >> SCB_SHCSR_PENDSVACT_Pos);
printf("\t Monitor active:%u\r\n",
(shcsr & SCB_SHCSR_MONITORACT_Msk) >> SCB_SHCSR_MONITORACT_Pos);
printf("\t SVCall active:%u\r\n",
(shcsr & SCB_SHCSR_SVCALLACT_Msk) >> SCB_SHCSR_SVCALLACT_Pos);
printf("\t Usage Fault active:%u\r\n",
(shcsr & SCB_SHCSR_USGFAULTACT_Msk) >> SCB_SHCSR_USGFAULTACT_Pos);
printf("\t Bus Fault active:%u\r\n",
(shcsr & SCB_SHCSR_BUSFAULTACT_Msk) >> SCB_SHCSR_BUSFAULTACT_Pos);
printf("\t Mem Fault active:%u\r\n",
(shcsr & SCB_SHCSR_MEMFAULTACT_Msk) >> SCB_SHCSR_MEMFAULTACT_Pos);
}

#define DIVBYZERO_Pos 9
#define DIVBYZERO_Msk (1ul << DIVBYZERO_Pos)
#define UNALIGNED_Pos 8
#define UNALIGNED_Msk (1ul << UNALIGNED_Pos)
#define NOCP_Pos 3
#define NOCP_Msk (1ul << NOCP_Pos)
#define INVPC_Pos 2
#define INVPC_Msk (1ul << INVPC_Pos)
#define INVSTATE_Pos 1
#define INVSTATE_Msk (1ul << INVSTATE_Pos)
#define UNDEFINSTR_Pos 0
#define UNDEFINSTR_Msk (1ul << UNDEFINSTR_Pos)
void dump_ufsr()
{
uint16_t ufsr = (SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk) >> SCB_CFSR_USGFAULTSR_Pos;
printf("UFSR:\r\n");
printf("\t Divide by zero: %d\r\n",
(ufsr & DIVBYZERO_Msk) >> DIVBYZERO_Pos);
printf("\t Unaligned access: %d\r\n",
(ufsr & UNALIGNED_Msk) >> UNALIGNED_Pos);
printf("\t No coprocessor: %d\r\n",
(ufsr & NOCP_Msk) >> NOCP_Pos);
printf("\t Invaild PC load: %d\r\n",
(ufsr & INVPC_Msk) >> INVPC_Pos);
printf("\t Invaild state(attempt to entry ARM state): %d\r\n",
(ufsr & INVSTATE_Msk) >> INVSTATE_Pos);
printf("\t Undefined instruction: %d\r\n",
(ufsr & UNDEFINSTR_Msk) >> UNDEFINSTR_Pos);
}

自造 UDP 协议栈

自造 UDP 协议栈

这一个星期一直在自造一个 UDP 协议栈.

今天是周五, 屁屁和室友逛街去了, 我就在寝室敲代码搞了一下午, 终于是搞定了.

这个是老师的项目里要用的, 实现 avr 的单片机和 PC 机通讯. 中间通过以太网进行连接, avr 上用的是 enc28j60 芯片. 这个芯片原来我在 C8051F 上写过一个驱动. 移植过来倒是不麻烦, 半天搞定.

麻烦的是协议栈!

原来在 51 上用的是一个我精简了的 uIP 协议栈, 因为 C8051F 系列 FLASH 足够大, 而且 ram 也大 (f340 有 4096 字节). 足够移植 uIP 过去了.

在 avr 上遇了不少麻烦.

首先就是蛋疼的 codevision 编译器乱改 C 语言, 不按标准来. const 修饰的变量没法直接访问. 移植起来很麻烦. 然后就是 uIP 的 ram 占用一直下不去. 我已经把以太网最大帧长度调成 512 了还是费了 1100 多个字节. 然后很不幸, ATMega16 只有 1024 字节的 RAM. 可恶的 uIP 竟然用去了 512 个字节… 知道 RAM 有多宝贵嘛你!

想想算了, 还是换个内存大点的吧. 周一给老师要了 ATMEGA32, 想着鸟枪换炮! 不料, 老师给了我张板子, 说” 自己焊吧”……

贴片的本身就不容易焊, 况且我烙铁吃灰 n 久了脏死了完全不想动呀! (我这个死洁癖!)

切! 想想我也是立志成为驱动工程师的男人, 这种小事应该交给硬件工程师来做嘛, 哼╭(╯^╰)╮(无任何冒犯…)

所以嘛这点困难完全难不倒我! 开源的不行我就自己写一个嘛.

自己动手丰衣足食.

↑一点都不华丽的分割线

自己造轮子的话最大的好处就是可以紧密把握需求, 只写需要的功能, 不需要的一概不写! 另外因为对内存需求比较严格, 所以这个协议栈几乎是不占内存, 除了 6 个 ip 地址 7 个 mac 地址常驻内存以外, 全部使用栈内存.

这个协议栈最大的特点就是简洁, 只有两个文件构成: udp.c, 和 udp.h
而且接口设计的也比较简洁, 只向外部提供三个函数:

1
2
3
4
void init_udp(unsigned char* mac_addr, ip_t ip_addr, ip_t netmask, ip_t gateway,
INCOMING_CALLBACK incoming, SENDBUF_CALLBACK sendbuf, DELAY_CALLBACK delay);
void process(unsigned char* buf, unsigned short len);
void send_udp(struct address* addr, void* buf, unsigned short len);

顾名思义

  1. init_udp 是初始化函数, 需要提供 ip 地址和 mac 地址. 另外需要提供三个回调函数.
  2. process 函数是当主程序接收到数据帧时需要调用的函数, 直接将以太网数据帧和 size 传入即可. 协议栈会进行处理 (现在可处理 arp 请求, icmp ping 请求, 和 udp 数据)
  3. send_udp 函数是主程序发送 udp 数据的函数, 需要把对端 ip 和端口以及本地端口存入 struct address 结构体中.

另外在 init_udp 函数中的三个回调函数:

1
2
3
typedef void (*INCOMING_CALLBACK)(struct address* addr, unsigned char* buf,  unsigned short len);
typedef void (*SENDBUF_CALLBACK)(void* buf, unsigned short len);
typedef void (*DELAY_CALLBACK)(int ms);

分别是

  1. 当有 UDP 数据到达时被调用
  2. 当需要发送数据时被调用
  3. 当需要 sleep 时调用

INCOMING_CALLBACK 函数的 addr 参数传来对端的 ip 端口和本地端口号, buf 中的是 UDP 数据, 不包含任何报头

SENDBUF_CALLBACK 函数传来 buf, SENDBUF_CALLBACK 需要将 buf 中的数据写入一个缓冲区 (可以是硬件缓冲区中). SENDBUF_CALLBACK 函数会被多次调用, 应当依次将 buf 中数据写入缓冲.

当 SENDBUF_CALLBACK 最后一次被调用时, buf 会传入 NULL. 此时, SENDBUF_CALLBACK 函数应当将缓冲区中数据发送到以太网上.

DELAY_CALLBACK 函数需要休眠指定的毫秒数, 如果程序运行在一个 RTOS 上的话可以调用 SLEEP 来让出处理器. 如果是裸板程序的话… 那就用循环吧 (这个协议栈在通过 arp 协议查找 mac 地址时会等待, 但最多不超过 1000 毫秒 [这个数值可以改]).

如果不能忍受等待的话, 可以将要紧任务放到中断中处理 (这个常识应该都用吧)

大致就是这样. 下午就是在板子上简单的测试了一下, 基本可用, 能收发 udp 包, 能 ping 通.

源码如下: (不做任何担保, 用了出事不怪我, 逃…

https://lengzzz.com/udp/udp-0.3.zip

最后上图:
image_1bl057v6q5uq1rbf1b5ejk311q9.png-579.9kB
image_1bl058hv31jct1gsd4slnej20dm.png-68.2kB

Proudly powered by Hexo and Theme by Hacker
© 2021 wastecat