姓名:
学号:
学院:信息学院
专业:通信工程
指导老师:
时间:2015年12月22日
通信系统工程实践报告
一、创建一个新的工程
1. Project →Project Wizard…→点击下一步→选择芯片→选择Toolsuite
并确保没有打叉,设置储存位置→点击下一步→创建新的工程文件,选择一个文件夹,确定后即可在这个文件家里找到自己创建的工程→添加文件或稍后创建文件后添加→完成。
2.File →New(或点击快捷栏New File 图标) →无论是.h 还是.c 文件,编辑完成后保存,保存时即可选择保存为.h 还是.c →往文件夹中添加刚刚保存的.h 、.c 文件即可
二、串口通信实验:
1)步骤
1、将IDE 3、Explorer 16主板、音频子板、音频线、扩音器、电脑连接好,打开主板电源;
2、打开MPLAB ,点击File →Open Workspace →DTMF_710A_ASM_REV - OK →dtmf_gen.mcw
3、此时,界面将出现层层叠叠的许多窗口,现在,点击Window →Tile Horizontally使各窗口水平排列或点击Window →Tile Vertically使各窗口垂直排列;
4、点击Programmer →Select Programmer→MPLAB ICD 3;
5、点击Project →Build All或点击快捷菜单栏中的Build All按钮,如果出现语言工具选择对话框,则一律选择上方的Use this;
6、点击Programmer →Program ,开始烧录程序;
7、打开串口调试助手,选择电脑此时的串口(打开设备管理器即可看到是哪一个COM 口;波特率选择19200;
8、在输入框中输入#*012~9字符,点击手动发送,则看到界面显示了输入的内容,主板上的显示频同样显示了输入的内容,同时听到拨号音。
9、将自动发送打钩,串口调试助手将自动循环发送输入的内容。减小自动发送周期,原本清晰连续的拨号音将变快,继续减小周期,将变成不断地噪音;
2)问题发现
1.Buil All 失败。
2. 通过实验发现,每次只能返回输入内容的前8位。
3. 在在自己的电脑上安装MPLAB 后打不开工作区。
3)问题解决
1. 对于Build All失败:点击Project →Select Language Tool Location→Microchip C30
Toolsuite 。
2. 对于只返回前8位:将串口调试助手的数据位由8位改至10位依然只返回前8位而不是前10位。再改为6位时,只返回前6位。由此可知,仅返回8位不仅和串口调试助手这款软件有关,还与实验程序有关,由于能力有限,我没有找到具体原因。
3. 对于打不开工作区文件,检查是否安装mplabc30-v3_31-windows-installer.exe,检查安装路径是否正确,检查工作区文件路径是否含有中文等或违规字符,经检查,问题出在我将工作区文件放在了一个中文名文件夹下,导致软件不能识别,修改后顺利打开。
三、 MPLAB 使用心得
1. 程序#Include 中modems.h 在file 窗口中未找到,这时可以在文件夹中搜索
2. 修改字体、字形、大小:Eidt →Properties... →Text →Select Font →在这个窗口中,即可修
改字体等参数,选择合适的字大小能方便阅读程序。
其实Eidt →Properties... 有更多功能,现例举如下:
3、在C File Types选项卡中,选中Line number,可以显示代码行序号,方便查看;
4、在C File Types选项卡中,取消Double click toggles breakpoint选项,可以取消双击鼠标设置断点,代替为双击选中选取单词,再次双击选取整行;
5、在C File Types选项卡中,选中Enable Code Floding选项,可以实现代码折叠功能;
6、在Tools tips 选项卡中,选中AutoComplete 下面的几个选项,可以在输入源代码的时候自动提示函数集结构体。
7、选中use tabedd window,实现可使打开地多个源文件,显示在一个标签栏上, 方便源文件切换。
8、Window 中Tile Horizontally/Vertically可以使窗口水平/垂直排列,方便浏览。效果如下:
四、dsPIC33F Demo for Explorer 16 Board程序理解
总体理解:程序始终在执行hours 、minutes 、seconds 的加法工作,由hexdec.c 程序提取出时分秒的十位数和个位数,再由一些汇编语言结合LATD 、TRISA 等寄存器使LCD 显示数字。本程序选择了带PLL 的在XT 晶振模式下的主振荡器,即使用了内置8MHz 的FRC 震荡器,分频后产生的FOSC 为32.4MHz 。由此获得了200us 、1ms 、2ms 、5ms 、15ms 以及1s 的延时。通过延时使得时分秒间隔各自所需延时加一,提取了十位数和个位数后再显示到了LCD 屏上。于是实现了实时时钟功能。
以下为详细程序理解(粗体为源程序,其余为我的理解):
_FOSCSEL(FNOSC_PRIPLL);
_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT);
_FWDT(FWDTEN_OFF);
在p33FJ256GP710A.h 中可查到如下定义:
#define FNOSC_PRIPLL 0xFFFB
转化为2进制:[**************]1,查手册知选用带PLL 的主(XT 、HS 或EC )振荡器
所以FNOSC_PRIPLL为带PLL 的主(XT 、HS 或EC )振荡器
#define FCKSM_CSDCMD 0xFFBF
转化为二进制:[**************],查手册知:FCKSM_CSDCMD为禁止时钟切换,禁止故障保护时钟监视器
#define OSCIOFNC_OFF 0xFFFF
转化为二进制:[**************]1查手册知OSCIOFNC_OFF表示 SC2 为时钟输出 #define POSCMD_XT 0xFFFD
转化为二进制:[**************]1查手册知POSCMD_XT为XT 晶振模式
#define FWDTEN_OFF 0xFF7F
转化为二进制:[**************]1查手册知FWDTEN_OFF即为通过用户软件使能/ 禁止看门狗定时器(可通过清零RCON 寄存器中的SWDTEN 位来禁止LPRC )
于是这三条语句实现的功能是:选用带PLL 的主(XT 、HS 或EC )振荡器,禁止时钟切换,禁止故障保护时钟监视器,SC2 为时钟输出,XT 晶振模式,通过用户软件使能/ 禁止看门狗定时器(可通过清零RCON 寄存器中的SWDTEN 位来禁止LPRC )
PLLFBD = 0x00A0;//给PLL 寄存器写入10100000
CLKDIV = 0x0048;//给ClDDIV 寄存器写入1001000
查手册中PLLFBD 寄存器表以及CLKDIV 表可知:
PLLFBD(PLL 反馈分频比寄存器) = 0x00A0; //10100000 162
bit 15-9 未实现:读为0
bit 8-0 PLLDIV:PLL 反馈分频比位(也表示为“M ”,PLL 倍频比)
010100000=162;
CLKDIV(时钟分频比寄存器) = 0x0048;//1001000
bit 15 ROI :中断恢复位 0 = 中断对DOZEN 位没有影响 000 = FCY /1 0 = 处理器时钟/ 外设时钟频率比强制为1:1 bit 14-12 DOZE:处理器时钟分频比选择位 bit 11 DOZEN :打盹模式使能位
bit 10-8 FRCDIV:内部快速RC 振荡器后分频比位
000 = FRC 被1 分频(默认)
由公式 可知
bit 7-6 PLLPOST:PLL VCO 输出分频比选择位(也表示为“N2”,PLL 后分频比) 01 = 输出/4 (默认) bit 5 未实现:读为0 bit 4-0 PLLPRE:PLL 相位检测器输入分频比位(也表示为“N1”,PLL 预分频比) 1000=输入/10
FOSC=8*(162/(10*4))=32.4MHz
由此可知FCY=FOSC/2=16.2MIPS LATA = 0xFF00;将发光二极管(d3-d10 / ra0-ra7)低驱动状态
TRISA = 0xFF00;集LED 引脚(d3-d10 / ra0-ra7)输出
home_clr();
调用函数lcd_cmd( 0x01 )
TRISD &= 0xFF00; 确保 RD0 - RD7 输出
DATA &= 0xFF00; 准备 RD0 - RD7
DATA |= cmd; lcd 的命令字符为0x01
RW = 0; 确保 RW 是 0
RS = 0;
E = 1; 切换线路E
Delay(Delay_5mS_Cnt); 延时5毫秒
puts_lcd( (char*) &mytext[0], sizeof(mytext) -1 ); LCD 第一行显示" xxxIC33F Demo ";
line_2();
puts_lcd( (char*) &mytext1[0], sizeof(mytext1) -1 ); LCD 第二行显示"Press S3 to cont"; 当然如果想要显示其他的信息,只要将之前定义的数组const char mytext[] = " xxxIC33F Demo ";const char mytext1[] = "Press S3 to cont"; 中的内容更改为想要的的即可。如将”xxxIC33F”改为“Hello World”,则将显示“Hello World”。注意不能改为中文,否则出现乱码,因为ASCII 表中没有中文。
while ( PORTDbits.RD6 )
当PORTDbits.RD6=1即当s3未按下的时候,程序在这里死循环,显示屏上只显示”press s3 to start”的欢迎语句。当s3按下,PORTDbits.RD6=0,跳出循环,执行下面的语句。
Init_Timer1();
在init_timer11中可找到函数定义:
void Init_Timer1( void )
{
int current_cpu_ipl; 声明临时变量存储CPU IPL
T1CON = 0;确保定时器1处于复位状态
IFS0bits.T1IF = 0;重置定时器1中断标志
IPC0bits.T1IP = 4;设置定时器1中断优先级为4
IEC0bits.T1IE = 1;启用定时器1中断
PR1 = 0x8000;设置定时器1周期寄存器
T1CONbits.TCS = 1; 选择外部定时器时钟
char a, b, c, *p;
a = 2;
b = 0x46;
c = 0x57;
p = (char *)&OSCCON;
以下程序使用32KHz 振荡器,低字节解锁顺序并使用LP 振荡器
asm volatile ("mov.b %1,[%0] \n" "mov.b %2,[%0] \n"
"mov.b %3,[%0] \n" : /* no outputs */ : "r"(p), "r"(b), "r"(c),
"r"(a));
以下程序执行解锁顺序后重新存储CPU IPL值
T1CONbits.TON = 1;启用定时器1和开始计数
#if SLEEP == 1 while ( TMR1
#endif 如果主循环将进入休眠模式,则在这里等待振荡器开始,
定时器开始计数
以上执行后禁用中断解锁序列 home_clr();
再次调用函数lcd_cmd( 0x01 );
void lcd_cmd( char cmd )
{
TRISD &= 0xFF00; 确保 RD0 - RD7 输出
DATA &= 0xFF00; 准备 RD0 - RD7
DATA |= cmd; lcd 的命令字符为0x01
RW = 0; 确保 RW 是 0
RS = 0;
E = 1; 切换线路E
Delay(Delay_5mS_Cnt); 延时5毫秒
}
puts_lcd( (char*) &time_msg[0], sizeof(time_msg) -1 );LCD 第一行显示"Time 00:00: 00 ";
line_2();
puts_lcd( (char*) &adc_msg1[0], sizeof(adc_msg1) -1 ); LCD第二行显示" RP5 = 0.00
Vdc "
Init_ADC();初始化ADC
程序接下来进入无限循环
while ( 1 )
{
if ( rtc_lcd_update )
{
hexdec( hours );
Update_LCD();
rtc_lcd_update = 0;
}
我认为程序的核心函数是以下函数
void Update_LCD( void )
{
hours LCD 行与列的光标位置设置如下 home_it();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
lcd_data(tens + 0x30);
(lcd data()函数定义如下
RW = 0; 确保RW 为0
RS = 1; 断言寄存器选择为1
DATA &= 0xFF00; 准备 RD0 - RD7
DATA |= data; LCD 的数据字节
E = 1;
Nop();汇编语言中的空语句,以占用时间
Nop();
Nop();
E = 0; 切换E 信号
RS = 0; 否定寄存器选择0
Delay_Us( Delay200uS_count ); 延时200us
Delay_Us( Delay200uS_count ); 延时200us )
lcd_data(ones + 0x30);
在lcd.c 文件中由控制LCD 信号寄存器引脚的宏定义可知RW 、RS 、E 等符号的意义:
#define RW LATDbits.LATD5 #define RS LATBbits.LATB15 #define E LATDbits.LATD4
由手册知定义RW 代表LATD 寄存器Bit5即LATD5
RW -> RD5 LATD寄存器控制LCD 灯RW 信号
RS -> RB15 LATD 寄存器控制LCD 灯RS 信号
E -> RD4 LATD 寄存器控制LCD 灯E 信号
#define RW_TRIS T RISDbits.TRISD5
#define RS_TRIS TRISBbits.TRISB15
#define E_TRIS TRISDbits.TRISD4
RW_TRIS->TRISD5
RS_TRIS -> TRISB15
E_TRIS -> TRISD4 #define DATA LATE
#define DATAPORT PORTE
#define TRISDATA TRISE
)
在lcd_data()中起到核心作用的延时函数定义如下:
void Delay( unsigned int delay_count )
{
temp_count = delay_count +1;
asm volatile("outer: dec _temp_count"); asm volatile("cp0 _temp_count"); asm volatile("bra z, done");
asm volatile("do #3200, inner" );
asm volatile("nop");
asm volatile("inner: nop"); asm volatile("bra outer");
asm volatile("done:"); }
void Delay_Us( unsigned int delayUs_count )
{ temp_count = delayUs_count +1; asm volatile("outer1: dec _temp_count");
asm volatile("cp0 _temp_count");
asm volatile("bra z, done1");
asm volatile("do #1500, inner1" );
asm volatile("nop");
asm volatile("inner1: nop");
asm volatile("bra outer1");
asm volatile("done1:");
}
这两段程序采用汇编语言编写,由于能力有限,没有深究。在头文件delay.h 中,定义FCY 为16000000,前述可知,FCY=16200000,经检查我没有发现我的问题所在,故我认为处处应定义FCY 为16200000。这个头文件分别定义了200us 、1ms 、2ms 、5ms 、15ms 、1s 。只需在lcd_data()、lcd_cmd()中改变调用的延时函数即可改变延时时间,如将lcd_cmd()中的Delay(Delay_5mS_Cnt);改为Delay(Delay_15mS_Cnt);即可延长三倍时间。故作类似更改就可以最终使LCD 上显示的时间变快或者变慢。
由hexdec.c 文件可知,tens 为hours 十位上的数,ones 为个位上的数
以下同理
minutes LCD行与列的光标位置设置如下
hexdec( minutes );将
cursor_right();
cursor_right();
lcd_data(tens + 0x30);
lcd_data(ones + 0x30);
seconds LCD行与列的光标位置设置如下
hexdec( seconds ); cursor_right();
cursor_right();
lcd_data(tens + 0x30);
lcd_data(ones + 0x30);
}
由isr_timer1.c知,当seconds 达到59时,再加一变为00,当minutes 达到59时,再加一变为00,当hours 达到23时,再加一变为00当IFS0bits.T1IF = 0时,重置Timer 1 中断。程序命令rtc_lcd_update = 1;以其为执行Update_LCD();的标志。同样,在程序isr_ADC.c中,定义了ADC 中断程序。
本程序未理解的部分:
1. 主程序中的if ( adc_lcd_update )
{
line_2();
advolt( temp1 );
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
lcd_data( adones );
cursor_right();
lcd_data( adtens );
lcd_data( adhunds );
adc_lcd_update = 0;
}
};
} 为什么实时时钟要用模数转换器呢,在实验中,仅观察到第一行时间的变化,第二行没有 变化,那么第二行"RP5 = 0.00 Vdc"提示的是什么信息?
2. 主程序是如何调用Timer1中断处理程序的,即
void __attribute__((__interrupt__)) _T1Interrupt( void )?
3.以上深色标注的部分,这些函数为什么要连续调用好几次?它们实现了怎样的功能。
4. 主程序中“tens + 0x30”、”ones + 0x30”,以及advolts.c 中”adones += 0x30;adtens += 0x30;adhunds += 0x30;adthous += 0x30;”等,据查这是汇编语言的知识,其目的是是Explorer 16 上LCD 显示数字,不过这需要了解LCD 的手册,故我没有再做深究,只知道在这里起了显示数字的作用。
希望能在今后更深入的学习中弄清楚未解决的问题。
五、课程感悟
通过这一阶段的学习,终于打开了Microchip 的大门。大一时只是在硬件上的连接,现在终于可以揭开现象去看本质了。踏进程序的大门地时候,瞬间被其严密的逻辑、精巧的结构所吸引。作为一个平台,Micorichip 有着无限的可能,我很期待在自己能在这方面成为一名巧匠,去开发自己的作品,雕琢出自己的璞玉。所以参考书、论坛交流、自主尝试是必不可少的。
在学习的过程中遇到的困难数不胜数,有时仅仅一句语言,就不仅要牵扯到宏定义,条件编译等抽象的软件知识,还要牵扯到寄存器,配置位,DMA 等硬件知识,以及软硬之间的联系,失之毫厘便谬以千里。如此庞大的知识体系,为数几周的学习仅仅只能掌握九牛一毛的知识,本文中也必定有诸多过失。但我已经很努力在学习Microchip 了,并且兴趣越来越浓,实践的过程也是试错的过程,这些错误必将使我走得更远。
最后感谢刘教授耐心教导、清楚又不失风趣的讲解!下一学期我一定还要抢到这门如此有趣的实践课!
姓名:
学号:
学院:信息学院
专业:通信工程
指导老师:
时间:2015年12月22日
通信系统工程实践报告
一、创建一个新的工程
1. Project →Project Wizard…→点击下一步→选择芯片→选择Toolsuite
并确保没有打叉,设置储存位置→点击下一步→创建新的工程文件,选择一个文件夹,确定后即可在这个文件家里找到自己创建的工程→添加文件或稍后创建文件后添加→完成。
2.File →New(或点击快捷栏New File 图标) →无论是.h 还是.c 文件,编辑完成后保存,保存时即可选择保存为.h 还是.c →往文件夹中添加刚刚保存的.h 、.c 文件即可
二、串口通信实验:
1)步骤
1、将IDE 3、Explorer 16主板、音频子板、音频线、扩音器、电脑连接好,打开主板电源;
2、打开MPLAB ,点击File →Open Workspace →DTMF_710A_ASM_REV - OK →dtmf_gen.mcw
3、此时,界面将出现层层叠叠的许多窗口,现在,点击Window →Tile Horizontally使各窗口水平排列或点击Window →Tile Vertically使各窗口垂直排列;
4、点击Programmer →Select Programmer→MPLAB ICD 3;
5、点击Project →Build All或点击快捷菜单栏中的Build All按钮,如果出现语言工具选择对话框,则一律选择上方的Use this;
6、点击Programmer →Program ,开始烧录程序;
7、打开串口调试助手,选择电脑此时的串口(打开设备管理器即可看到是哪一个COM 口;波特率选择19200;
8、在输入框中输入#*012~9字符,点击手动发送,则看到界面显示了输入的内容,主板上的显示频同样显示了输入的内容,同时听到拨号音。
9、将自动发送打钩,串口调试助手将自动循环发送输入的内容。减小自动发送周期,原本清晰连续的拨号音将变快,继续减小周期,将变成不断地噪音;
2)问题发现
1.Buil All 失败。
2. 通过实验发现,每次只能返回输入内容的前8位。
3. 在在自己的电脑上安装MPLAB 后打不开工作区。
3)问题解决
1. 对于Build All失败:点击Project →Select Language Tool Location→Microchip C30
Toolsuite 。
2. 对于只返回前8位:将串口调试助手的数据位由8位改至10位依然只返回前8位而不是前10位。再改为6位时,只返回前6位。由此可知,仅返回8位不仅和串口调试助手这款软件有关,还与实验程序有关,由于能力有限,我没有找到具体原因。
3. 对于打不开工作区文件,检查是否安装mplabc30-v3_31-windows-installer.exe,检查安装路径是否正确,检查工作区文件路径是否含有中文等或违规字符,经检查,问题出在我将工作区文件放在了一个中文名文件夹下,导致软件不能识别,修改后顺利打开。
三、 MPLAB 使用心得
1. 程序#Include 中modems.h 在file 窗口中未找到,这时可以在文件夹中搜索
2. 修改字体、字形、大小:Eidt →Properties... →Text →Select Font →在这个窗口中,即可修
改字体等参数,选择合适的字大小能方便阅读程序。
其实Eidt →Properties... 有更多功能,现例举如下:
3、在C File Types选项卡中,选中Line number,可以显示代码行序号,方便查看;
4、在C File Types选项卡中,取消Double click toggles breakpoint选项,可以取消双击鼠标设置断点,代替为双击选中选取单词,再次双击选取整行;
5、在C File Types选项卡中,选中Enable Code Floding选项,可以实现代码折叠功能;
6、在Tools tips 选项卡中,选中AutoComplete 下面的几个选项,可以在输入源代码的时候自动提示函数集结构体。
7、选中use tabedd window,实现可使打开地多个源文件,显示在一个标签栏上, 方便源文件切换。
8、Window 中Tile Horizontally/Vertically可以使窗口水平/垂直排列,方便浏览。效果如下:
四、dsPIC33F Demo for Explorer 16 Board程序理解
总体理解:程序始终在执行hours 、minutes 、seconds 的加法工作,由hexdec.c 程序提取出时分秒的十位数和个位数,再由一些汇编语言结合LATD 、TRISA 等寄存器使LCD 显示数字。本程序选择了带PLL 的在XT 晶振模式下的主振荡器,即使用了内置8MHz 的FRC 震荡器,分频后产生的FOSC 为32.4MHz 。由此获得了200us 、1ms 、2ms 、5ms 、15ms 以及1s 的延时。通过延时使得时分秒间隔各自所需延时加一,提取了十位数和个位数后再显示到了LCD 屏上。于是实现了实时时钟功能。
以下为详细程序理解(粗体为源程序,其余为我的理解):
_FOSCSEL(FNOSC_PRIPLL);
_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT);
_FWDT(FWDTEN_OFF);
在p33FJ256GP710A.h 中可查到如下定义:
#define FNOSC_PRIPLL 0xFFFB
转化为2进制:[**************]1,查手册知选用带PLL 的主(XT 、HS 或EC )振荡器
所以FNOSC_PRIPLL为带PLL 的主(XT 、HS 或EC )振荡器
#define FCKSM_CSDCMD 0xFFBF
转化为二进制:[**************],查手册知:FCKSM_CSDCMD为禁止时钟切换,禁止故障保护时钟监视器
#define OSCIOFNC_OFF 0xFFFF
转化为二进制:[**************]1查手册知OSCIOFNC_OFF表示 SC2 为时钟输出 #define POSCMD_XT 0xFFFD
转化为二进制:[**************]1查手册知POSCMD_XT为XT 晶振模式
#define FWDTEN_OFF 0xFF7F
转化为二进制:[**************]1查手册知FWDTEN_OFF即为通过用户软件使能/ 禁止看门狗定时器(可通过清零RCON 寄存器中的SWDTEN 位来禁止LPRC )
于是这三条语句实现的功能是:选用带PLL 的主(XT 、HS 或EC )振荡器,禁止时钟切换,禁止故障保护时钟监视器,SC2 为时钟输出,XT 晶振模式,通过用户软件使能/ 禁止看门狗定时器(可通过清零RCON 寄存器中的SWDTEN 位来禁止LPRC )
PLLFBD = 0x00A0;//给PLL 寄存器写入10100000
CLKDIV = 0x0048;//给ClDDIV 寄存器写入1001000
查手册中PLLFBD 寄存器表以及CLKDIV 表可知:
PLLFBD(PLL 反馈分频比寄存器) = 0x00A0; //10100000 162
bit 15-9 未实现:读为0
bit 8-0 PLLDIV:PLL 反馈分频比位(也表示为“M ”,PLL 倍频比)
010100000=162;
CLKDIV(时钟分频比寄存器) = 0x0048;//1001000
bit 15 ROI :中断恢复位 0 = 中断对DOZEN 位没有影响 000 = FCY /1 0 = 处理器时钟/ 外设时钟频率比强制为1:1 bit 14-12 DOZE:处理器时钟分频比选择位 bit 11 DOZEN :打盹模式使能位
bit 10-8 FRCDIV:内部快速RC 振荡器后分频比位
000 = FRC 被1 分频(默认)
由公式 可知
bit 7-6 PLLPOST:PLL VCO 输出分频比选择位(也表示为“N2”,PLL 后分频比) 01 = 输出/4 (默认) bit 5 未实现:读为0 bit 4-0 PLLPRE:PLL 相位检测器输入分频比位(也表示为“N1”,PLL 预分频比) 1000=输入/10
FOSC=8*(162/(10*4))=32.4MHz
由此可知FCY=FOSC/2=16.2MIPS LATA = 0xFF00;将发光二极管(d3-d10 / ra0-ra7)低驱动状态
TRISA = 0xFF00;集LED 引脚(d3-d10 / ra0-ra7)输出
home_clr();
调用函数lcd_cmd( 0x01 )
TRISD &= 0xFF00; 确保 RD0 - RD7 输出
DATA &= 0xFF00; 准备 RD0 - RD7
DATA |= cmd; lcd 的命令字符为0x01
RW = 0; 确保 RW 是 0
RS = 0;
E = 1; 切换线路E
Delay(Delay_5mS_Cnt); 延时5毫秒
puts_lcd( (char*) &mytext[0], sizeof(mytext) -1 ); LCD 第一行显示" xxxIC33F Demo ";
line_2();
puts_lcd( (char*) &mytext1[0], sizeof(mytext1) -1 ); LCD 第二行显示"Press S3 to cont"; 当然如果想要显示其他的信息,只要将之前定义的数组const char mytext[] = " xxxIC33F Demo ";const char mytext1[] = "Press S3 to cont"; 中的内容更改为想要的的即可。如将”xxxIC33F”改为“Hello World”,则将显示“Hello World”。注意不能改为中文,否则出现乱码,因为ASCII 表中没有中文。
while ( PORTDbits.RD6 )
当PORTDbits.RD6=1即当s3未按下的时候,程序在这里死循环,显示屏上只显示”press s3 to start”的欢迎语句。当s3按下,PORTDbits.RD6=0,跳出循环,执行下面的语句。
Init_Timer1();
在init_timer11中可找到函数定义:
void Init_Timer1( void )
{
int current_cpu_ipl; 声明临时变量存储CPU IPL
T1CON = 0;确保定时器1处于复位状态
IFS0bits.T1IF = 0;重置定时器1中断标志
IPC0bits.T1IP = 4;设置定时器1中断优先级为4
IEC0bits.T1IE = 1;启用定时器1中断
PR1 = 0x8000;设置定时器1周期寄存器
T1CONbits.TCS = 1; 选择外部定时器时钟
char a, b, c, *p;
a = 2;
b = 0x46;
c = 0x57;
p = (char *)&OSCCON;
以下程序使用32KHz 振荡器,低字节解锁顺序并使用LP 振荡器
asm volatile ("mov.b %1,[%0] \n" "mov.b %2,[%0] \n"
"mov.b %3,[%0] \n" : /* no outputs */ : "r"(p), "r"(b), "r"(c),
"r"(a));
以下程序执行解锁顺序后重新存储CPU IPL值
T1CONbits.TON = 1;启用定时器1和开始计数
#if SLEEP == 1 while ( TMR1
#endif 如果主循环将进入休眠模式,则在这里等待振荡器开始,
定时器开始计数
以上执行后禁用中断解锁序列 home_clr();
再次调用函数lcd_cmd( 0x01 );
void lcd_cmd( char cmd )
{
TRISD &= 0xFF00; 确保 RD0 - RD7 输出
DATA &= 0xFF00; 准备 RD0 - RD7
DATA |= cmd; lcd 的命令字符为0x01
RW = 0; 确保 RW 是 0
RS = 0;
E = 1; 切换线路E
Delay(Delay_5mS_Cnt); 延时5毫秒
}
puts_lcd( (char*) &time_msg[0], sizeof(time_msg) -1 );LCD 第一行显示"Time 00:00: 00 ";
line_2();
puts_lcd( (char*) &adc_msg1[0], sizeof(adc_msg1) -1 ); LCD第二行显示" RP5 = 0.00
Vdc "
Init_ADC();初始化ADC
程序接下来进入无限循环
while ( 1 )
{
if ( rtc_lcd_update )
{
hexdec( hours );
Update_LCD();
rtc_lcd_update = 0;
}
我认为程序的核心函数是以下函数
void Update_LCD( void )
{
hours LCD 行与列的光标位置设置如下 home_it();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
lcd_data(tens + 0x30);
(lcd data()函数定义如下
RW = 0; 确保RW 为0
RS = 1; 断言寄存器选择为1
DATA &= 0xFF00; 准备 RD0 - RD7
DATA |= data; LCD 的数据字节
E = 1;
Nop();汇编语言中的空语句,以占用时间
Nop();
Nop();
E = 0; 切换E 信号
RS = 0; 否定寄存器选择0
Delay_Us( Delay200uS_count ); 延时200us
Delay_Us( Delay200uS_count ); 延时200us )
lcd_data(ones + 0x30);
在lcd.c 文件中由控制LCD 信号寄存器引脚的宏定义可知RW 、RS 、E 等符号的意义:
#define RW LATDbits.LATD5 #define RS LATBbits.LATB15 #define E LATDbits.LATD4
由手册知定义RW 代表LATD 寄存器Bit5即LATD5
RW -> RD5 LATD寄存器控制LCD 灯RW 信号
RS -> RB15 LATD 寄存器控制LCD 灯RS 信号
E -> RD4 LATD 寄存器控制LCD 灯E 信号
#define RW_TRIS T RISDbits.TRISD5
#define RS_TRIS TRISBbits.TRISB15
#define E_TRIS TRISDbits.TRISD4
RW_TRIS->TRISD5
RS_TRIS -> TRISB15
E_TRIS -> TRISD4 #define DATA LATE
#define DATAPORT PORTE
#define TRISDATA TRISE
)
在lcd_data()中起到核心作用的延时函数定义如下:
void Delay( unsigned int delay_count )
{
temp_count = delay_count +1;
asm volatile("outer: dec _temp_count"); asm volatile("cp0 _temp_count"); asm volatile("bra z, done");
asm volatile("do #3200, inner" );
asm volatile("nop");
asm volatile("inner: nop"); asm volatile("bra outer");
asm volatile("done:"); }
void Delay_Us( unsigned int delayUs_count )
{ temp_count = delayUs_count +1; asm volatile("outer1: dec _temp_count");
asm volatile("cp0 _temp_count");
asm volatile("bra z, done1");
asm volatile("do #1500, inner1" );
asm volatile("nop");
asm volatile("inner1: nop");
asm volatile("bra outer1");
asm volatile("done1:");
}
这两段程序采用汇编语言编写,由于能力有限,没有深究。在头文件delay.h 中,定义FCY 为16000000,前述可知,FCY=16200000,经检查我没有发现我的问题所在,故我认为处处应定义FCY 为16200000。这个头文件分别定义了200us 、1ms 、2ms 、5ms 、15ms 、1s 。只需在lcd_data()、lcd_cmd()中改变调用的延时函数即可改变延时时间,如将lcd_cmd()中的Delay(Delay_5mS_Cnt);改为Delay(Delay_15mS_Cnt);即可延长三倍时间。故作类似更改就可以最终使LCD 上显示的时间变快或者变慢。
由hexdec.c 文件可知,tens 为hours 十位上的数,ones 为个位上的数
以下同理
minutes LCD行与列的光标位置设置如下
hexdec( minutes );将
cursor_right();
cursor_right();
lcd_data(tens + 0x30);
lcd_data(ones + 0x30);
seconds LCD行与列的光标位置设置如下
hexdec( seconds ); cursor_right();
cursor_right();
lcd_data(tens + 0x30);
lcd_data(ones + 0x30);
}
由isr_timer1.c知,当seconds 达到59时,再加一变为00,当minutes 达到59时,再加一变为00,当hours 达到23时,再加一变为00当IFS0bits.T1IF = 0时,重置Timer 1 中断。程序命令rtc_lcd_update = 1;以其为执行Update_LCD();的标志。同样,在程序isr_ADC.c中,定义了ADC 中断程序。
本程序未理解的部分:
1. 主程序中的if ( adc_lcd_update )
{
line_2();
advolt( temp1 );
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
cursor_right();
lcd_data( adones );
cursor_right();
lcd_data( adtens );
lcd_data( adhunds );
adc_lcd_update = 0;
}
};
} 为什么实时时钟要用模数转换器呢,在实验中,仅观察到第一行时间的变化,第二行没有 变化,那么第二行"RP5 = 0.00 Vdc"提示的是什么信息?
2. 主程序是如何调用Timer1中断处理程序的,即
void __attribute__((__interrupt__)) _T1Interrupt( void )?
3.以上深色标注的部分,这些函数为什么要连续调用好几次?它们实现了怎样的功能。
4. 主程序中“tens + 0x30”、”ones + 0x30”,以及advolts.c 中”adones += 0x30;adtens += 0x30;adhunds += 0x30;adthous += 0x30;”等,据查这是汇编语言的知识,其目的是是Explorer 16 上LCD 显示数字,不过这需要了解LCD 的手册,故我没有再做深究,只知道在这里起了显示数字的作用。
希望能在今后更深入的学习中弄清楚未解决的问题。
五、课程感悟
通过这一阶段的学习,终于打开了Microchip 的大门。大一时只是在硬件上的连接,现在终于可以揭开现象去看本质了。踏进程序的大门地时候,瞬间被其严密的逻辑、精巧的结构所吸引。作为一个平台,Micorichip 有着无限的可能,我很期待在自己能在这方面成为一名巧匠,去开发自己的作品,雕琢出自己的璞玉。所以参考书、论坛交流、自主尝试是必不可少的。
在学习的过程中遇到的困难数不胜数,有时仅仅一句语言,就不仅要牵扯到宏定义,条件编译等抽象的软件知识,还要牵扯到寄存器,配置位,DMA 等硬件知识,以及软硬之间的联系,失之毫厘便谬以千里。如此庞大的知识体系,为数几周的学习仅仅只能掌握九牛一毛的知识,本文中也必定有诸多过失。但我已经很努力在学习Microchip 了,并且兴趣越来越浓,实践的过程也是试错的过程,这些错误必将使我走得更远。
最后感谢刘教授耐心教导、清楚又不失风趣的讲解!下一学期我一定还要抢到这门如此有趣的实践课!