星期三, 十月 25, 2006



这里列出了PIC单片机重的一些常用程序和方法

1. 关于org的使用例子:

在这个例子中用了两次org伪指令,ORG 0 和 ORG 1FFH, 前者是程序开始的地方。
而后者的位置是芯片复位的位置。

ORG 0 ; 程序从零地址开始
MAIN:
;..............
GOTO MAIN ; 主循环
;-----------------------------
ORG 1FFH
GOTO MAIN
;-----------------------------
END ;程序结束


2. BCD码转换

;--方法 1--
;----------------------------------------------------------
; Function: 转换为BCD码显示和存储
;----------------------------------------------------------
BCD2:
MOVLW 8;
MOVWF COUNT_BIT ;循环左移计数器置8
CLRF BCD_BUF1 ;清除缓冲器
MOVF DISPLAY_NUM,W
MOVWF BCD_BUF2
LOOP1:
RLF BCD_BUF2,1 ;二进制数转换成BCD码(以便显示)
RLF BCD_BUF1,1
DECFSZ COUNT_BIT,1 ;左移8次
GOTO ADJUST
MOVF BCD_BUF1,W
MOVWF DISPBUF
MOVF DISPBUF,W
RETURN
ADJUST:
MOVLW 3 ;二进制转BCD的调整
ADDWF BCD_BUF1,W ;每次移位后都检查低四位LSD+3是否大于7
MOVWF BCD_SUM
BTFSC BCD_SUM,3
MOVWF BCD_BUF1
MOVLW 30H
ADDWF BCD_BUF1,W ;如果是则再加3,否则不加。
MOVWF BCD_SUM
BTFSC BCD_SUM,7
MOVWF BCD_BUF1 ;接着再将高四位MSD作相同处理
GOTO LOOP1

;--方法 2--
;----------------------------------------------------------
; Function: 8位二进制数转换成2位BCD码
; 转换为两个BCD码:BCD_MSD,BCD_LSD
;----------------------------------------------------------
BIN2BCD:
CLRF BCD_MSD
MOVWF BCD_LSD
GETENTH:
MOVLW 10
SUBWF BCD_LSD,0
BTFSS STATUS,C
GOTO OVER
MOVWF BCD_LSD
INCF BCD_MSD,1
GOTO GETENTH
OVER:
RETURN

3. 延时函数


;--- 所用的临时变量----
DELAYH EQU 023H
DELAYL EQU 024H
;----------------------------------------------------------
; Function: 延时
;----------------------------------------------------------
DELAY:
MOVLW 0FFH
MOVWF DELAYH
DELAY_LOOP:
MOVLW 0FFH
MOVWF DELAYL
DELAY_LOOP1:
NOP
DECFSZ DELAYL
GOTO DELAY_LOOP1
DECFSZ DELAYH
GOTO DELAY_LOOP
RETURN


4. 中断处理程序的设计


【一】 ;中断的入口地址
ORG 0004H
GOTO INTSERVE

然后在INTSERVE地址写入判断中断入口的代码:
INTSERVE: MOVWF W_TEMP ;保存寄存器
SWAPF STATUS,W ;注意使用SWAPF指令,才不会影响Z标志位。
BCF STATUS,RP0
MOVWF STATUS_TEMP
这样W,STATUS分别保存到W_TEMP,STATUS_TEMP两个地方了。

【二】 ;中断的出口处理:恢复W,STATUS两个寄存器
;所有的中断处理结束后都必须这样处理。
SWAPF STATUS_TEMP,W
MOVWF STATUS
SWAPF W_TEMP,1
SWAPF W_TEMP,W
RETFIE ;中断返回

【三】 ;中断的判断中断源,分别进入特定的处理程序
BTFSC PIR1,ADIF ;A/D转换中断
GOTO ADINTSERV
BTFSC PIR1,RCIF ;串口接收中断
GOTO RCINTSERV
BTFSC PIR1,TMR2IF ;定时器2中断
GOTO TIMER2SERV
GOTO RETFIEO ;直接到中断的出口处理

【四】 ;特定中断的处理
TIMER2SERV ;定时器2的中断处理
BCF PIR1,TMR2IF ;清除中断标志位
……
GOTO RETFIEO ;直接到中断的出口处理



5. A/D转换的程序设计


【一】各个寄存器的使用

ADRESH : A/D转换结果高字节
ADRESL : A/D转换结果低字节
ADCON0 : 控制A/D操作
ADCON1 : 控制A/D引脚

(1)ADCON0 地址:1FH
Bit7 Bit0
+-------+-------+------+------+------+------------+----+------+
| ADCS1 | ADCS0 | CHS2 | CHS1 | CHS0 | GO/DONE- | -- | ADON |
+-------+-------+------+------+------+------------+----+------+
__________/ | |
| | +-在工作或者被关闭
| +--1:正在进行A/D转换
+---A/D时钟选择,00,01,10分别表示2,8,32分频,或者11用内部RC振荡器

CHS2, CHS1,CHS0用来选择模拟通道

(2)ADCON1 地址:9FH
Bit7 Bit0
+-------+-----+-----+-----+-------+-------+-------+-------+
| ADFM | --- | --- | --- | PCFG3 | PCFG2 | PCFG1 | PCFG0 |
+-------+-----+-----+-----+-------+-------+-------+-------+
| ______________________/
| |
| +-端口配置
++- 1:右移,ADRESH高六位读作零
+- 0:左移,ADRESL低六位读作零

(3)ADRESH(地址:1EH),ADRESL(地址:9EH)

(4)TRISA : PORTA的方向寄存器设置
选择好A/D端口号后,必须在A/D转换开始之前设置号TRISA,
使得A/D输入通道为输入态。

【二】当A/D转换结束
A/D转换完成了以后,10位的A/D结果存放在ADRESH,ADRESL,同时GO/DONE-将被清零。
而且,A/D转换中断标志位ADIF位将被置一。

【三】实现A/D转换的步骤
1)设置A/D转换模块
a、对模拟引脚、基准电压、数字I/O(ADCON1)进行设置
b、选择A/D输入通道 ADCON0
c、选择A/D转换时钟 ADCON0
d、打开A/D转换模块 ADCON0
2)如果需要A/D转换中断功能,设置A/D中断
a、对A/D转换完成标志位ADIF清零。
b、对A/D转换中断使能位ADIE置一。
c、对PEIE位置一。
d、对全局中断使能位GIE置一。
3)所需等待采样时间
4)对GO/DONE-置一,启动A/D转换。
5)等待A/D转换完成,有两种方法:
a、软件查询GO/DONE-位的状态是否为1
b、等待A/D转换完成的中断。
6)读入A/D结果 ADRESH,ADRESL。如果为中断处理就清除ADIF位。



星期一, 十月 02, 2006

OS说:要更多并发,于是有了多线程

这里我将说明一下自己在使用pthread遇到的一些问题,顺便作为这里长期空白的补丁。

先做一个广告好了。我的系统是ubuntu6.06 Dapper Drake,相信现在使用这个发行版本的用户数量是最为庞大的。而且由于Debian的强大支持,这个系统在安装和使用中都是非常适合入门,以及拥有相当Linux知识的人士使用。不过默认情况下是没有gcc和make这些东西的,只有一个链接器ld被安装。所以,之前请安装它们两个软件。(用sudo执行apt-get install gcc make 即可。)不过如果你需要边听着mp3边工作的话,请自己搜索一下让rhythmbox支持它的方法吧。

网络知识资源:pthread入门 这是我强力推荐的站点。
IBM的developerWorks的thread专栏 下面也有很多关于Linux的线程问题的讨论,用心挖掘会了解很多有益的东西的。
当然,如果手边有一本像样的pthread书籍的话那就更加完美了。

现在可以创建你的C文件,并include像是unistd.h, stdio.h, 当然还有pthread.h这些文件。

如何创建线程?
只用这一句即可:
iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
不过里面的参数却要说明一下。
首先是thread1这个参数,它是pthread_t类型的,作为返回值告诉我们创建的这个线程编号。而print_message_function和message1则是线程函数和它的入参了。返回值表示成功与否。

然后呢?
当然是线程开始工作。但是主线程,也就是创建它的那个线程该怎么办呢?它的使命结束了吗?
我们可以在创建一个线程后结束自己,然后看看会怎么样。
int main()
{
pthread_t threadid;
int ret;

ret = pthread_create( &threadid, NULL,
print_message_function, (void*)msgarg);
//pthread_join(threadid, NULL);
printf("I, the father process(%d) is exitting..\n", getpid());
exit(0);
}

void *print_message_function( void *ptr )
{
sleep(5);
puts((const char*) ptr);
return NULL;
}
结果就是完全看不到线程输出的信息,查看进程表的结果也同样说明了这个问题。主线程的结束将终结所有子线程。
这和在网络上看到一些言论不太一样,据报道有人在主线程先于子线程结束的情况下发现整个进程变成了“僵尸”
不过这里没有发现这种情况。

但是一般意义上,子线程的退出应该是可以预料的,比如使用一个全局的符号或者消息来通知它们。而不是如此粗暴的关闭程序,这样我们无法全面释放任务线程所占用的资源,这时候就需要在上面一段中被注释的那一行代码了--使用pthread_join来等待创建的线程结束。
(关于资源的占用和释放的主题请关注pthread_cleanup_push和pthread_cleanup_pop的相关内容。)

(待续……)