历史上的今天
返回首页

历史上的今天

今天是:2024年11月26日(星期二)

正在发生

2019年11月26日 | PIC单片机C语言简记

2019-11-26 来源:eefocus

1.PICC安装:    

  PICC编译器可以直接挂接在MPLAB-IDE集成开发平台下,实现一体化的编译连接和原代码调试。使用MPLAB-IDE内的调试工具ICE2000 、ICD2 和软件模拟器都可以实现原代码级的程序调试,非常方便。


  首先必须在你的计算机中安装 PICC 编译器。安装成功后可以进入IDE ,选择菜单项Project Æ Set Language Tool Locations… ,打开语言工具挂接设置对话框。在对话框中选择“HI-TECH PICC Toolsuite”栏,展开可执行文件组“Executable”后,列出了将被MPLAB-IDE 后台调用的编译器所用到的所有可执行文件,其中有汇编编译器“PICC Assembler ”、C 原程序编译器“PICC Compiler”和连接定位程序“PICC Linker”。同时在此列表中还显示了对应的可执行程序名,请注意在这里都是“PICC.EXE”。用鼠标分别点击选中这三项可执行文件,观察对话框下面“Location ”一栏中显示的文件路径,用“Browse…”按纽,从计算机中已经安装的 PICC编译器文件夹中选择PICC.EXE 文件。


     实际上PICC.EXE 只是一个调度管理程序,它会按照所输入的文件扩展名自动调用对应的编译器和连接器,用户要注意的是C 语言原程序扩展名用“.c ”,汇编原程序用“.as”即可。用C 语言编程的好处是可以实现模块化编程。程序编写者应尽量把相互独立的控制任务用多个独立的C 原程序文件实现,如果程序量较大,一般不要把所有的代码写在一个文件内。


  基于PICC编译环境编写PIC 单片机程序的基本方式和标准C 程序类似,程序一般由以下几个主要部分组成: 
  z   在程序的最前面用#include 预处理指令引用包含头文件,其中必须包含一个编译器提供的“pic.h ”文件,实现单片机内特殊寄存器和其它特殊符号的声明; 
  z   用“__CONFIG ”预处理指令定义芯片的配置位; 
  z   声明本模块内被调用的所有函数的类型,PICC将对所调用的函数进行严格的类型匹配检查; 
  z   定义全局变量或符号替换; 
  z   实现函数(子程序),特别注意 main 函数必须是一个没有返回的死循环。


 1 #include    //包含单片机内部资源预定义 

 2 #include “pc68.h”    //包含自定义头文件 

 3  //定义芯片工作时的配置位 

 4 __CONFIG (HS & PROTECT & PWRTEN & BOREN & WDTDIS); 

 5  //声明本模块中所调用的函数类型 

 6 void SetSFR(void); 

 7 void Clock(void); 

 8 void KeyScan(void); 

 9 void Measure(void); 

10 void LCD_Test(void); 

11 void LCD_Disp(unsigned char);  

12 //定义变量 

13 unsigned char second, minute, hour; 

14 bit flag1,flag2; 

15 //函数和子程序

16 void main(void) 

17 { 

18   SetSFR(); 

19   PORTC = 0x00; 

20   TMR1H += TMR1H_CONST; 

21   LED1 = LED_OFF; 

22 

23   LCD_Test(); 

24  

25   //程序工作主循环 

26   while(1) { 

27      asm(“clrwdt”);   //清看门狗 

28      Clock();        //更新时钟 

29      KeyScan();      //扫描键盘 

30      Measure();      //数据测量 

31      SetSFR();       //刷新特殊功能寄存器 

32   } 

33 } 


2.PICC 中的变量定义:

bit         1      布尔型位变量,0 或1 两种取值 
char          8      有符号或无符号字符变量,PICC 缺省认定char 型变量为无符号数,但可以通过编译选项改为有符号字节变量 
unsigned char    8      无符号字符变量 
short       16      有符号整型数 
unsigned short   16      无符号整型数 
int           16      有符号整型数 
unsigned int         16      无符号整型数 
long        32      有符号长整型数 
unsigned long       32      无符号长整型数 
float        24      浮点数 
double       24  或32     浮点数,PICC 缺省认定double 型变量为24位长,但可以改变编译选项改成32位


除了bit型位变量外,PICC完全支持数组、结构和联合等复合型高级变量,这和标准的C 语言所支持的高级变量类型没有什么区别。例如: 

数组:unsigned int data[10]; 

结构:struct commInData { 

         unsigned char inBuff[8]; 

         unsigned char getPtr, putPtr; 

      }; 

联合:union int_Byte { 

         unsigned char c[2]; 

         unsigned int i; 

      }; 

3.PICC对数据寄存器bank 的管理

  PICC把单片机中数据寄存器的bank 问题交由编程员自己管理,因此在定义用户变量时你必须自己决定这些变量具体放在哪一个bank 中。如果没有特别指明,所定义的变量将被定位在bank0。  


例如下面所定义的这些变量: 

unsigned char buffer[32]; 

bit flag1,flag2; 

float val[8]; 


  除了bank0 内的变量声明时不需特殊处理外,定义在其它bank 内的变量前面必须加上相应的bank 序号,例如: 

bank1 unsigned char buffer[32];    //变量定位在bank1中 
bank2 bit flag1,flag2;    //变量定位在bank2中 
bank3 float val[8];    //变量定位在bank3中

  如果超过bank 容量,在最后连接时会报错,大致信息如下:(中档PIC中一个数据寄存器128字节) 
Error[000]   : Can't find 0x12C words for psect rbss_1 in segment BANK1

 
   连接器告诉你总共有0x12C (300 )个字节准备放到 bank1 中但 bank1 容量不够。显然,只有把一部分原本定位在bank1 中的变量改放到其它 bank 中才能解决此问题。


  虽然变量所在的bank 定位必须由编程员自己决定,但在编写原程序时进行变量存取操作前无需再特意编写设定bank 的指令。C 编译器会根据所操作的对象自动生成对应 bank 设定的汇编指令。为避免频繁的bank 切换以提高代码效率,尽量把实现同一任务的变量定位在同一个bank 内;对不同bank 内的变量进行读写操作时也尽量把位于相同 bank 内的变量归并在一起进行连续操作。


    PICC中把所有的函数内部定义的auto型局部变量放在bank0。为了节约宝贵的存储空间,它采用了一种被叫做“静态覆盖”的技术来实现局部变量的地址分配。因此用户自己定位在bank()内的变量字节数将受到一定的限制,在实际使用时需注意。


   bit型为变量只能是全局的或静态的。PICC将把定位在同一bank内的8个位变量合并成一个存放于一个固定地址。因此所有指针对为变量的操作将直接使用PIC单片机的位操作汇编指令高效实现。

 

  在用C 语言写程序时变量一般由编译器和连接器最后定位,在写程序之时无需知道所定义的变量具体被放在哪个地址(除了bank 必须声明)。真正需要绝对定位的只是单片机中的那些特殊功能寄存器,而这些寄存器的地址定位在PICC编译环境所提供的头文件中已经实现,无需用户操心。


  unsigned char tmpData @ 0x20;  //tmpData定位在地址0x20


    上面变量 tmpData 的地址是0x20,但最后 0x20 处完全有可能又被分配给了其它变量使用,这样就发生了地址冲突。因此针对变量的绝对定位要特别小心。从笔者的应用经验看,在一般的程序设计中用户自定义的变量实在是没有绝对定位的必要。


  如果需要,位变量也可以绝对定位。但必须遵循上面介绍的位变量编址的方式。如果一个普通变量已经被绝对定位,那么此变量中的每个数据位就可以用下面的计算方式实现位变量指派: 
      unsigned char tmpData @ 0x20;  //tmpData定位在地址0x20 
      bit tmpBit0 @ tmpData*8+0;    //tmpBit0对应于tmpData第0 位 
      bit tmpBit1 @ tmpData*8+1;    //tmpBit0对应于tmpData第1 位 
      bit tmpBit2 @ tmpData*8+2;    //tmpBit0对应于tmpData第2 位 
如果tmpData 事先没有被绝对定位,那就不能用上面的位变量定位方式。


4.PICC的其它变量修饰关键词: 

  extern  —  外部变量声明。如果在一个C 程序文件中要使用一些变量但其原型定义写在另外的文件中,那么在本文件中必须将这些变量声明成“extern ”外部类型。


例如程序文件code1.c中有如下定义: 

bank1 unsigned char var1, var2;      //定义了bank1中的两个变量 

在另外一个程序文件code2.c中要对上面定义的变量进行操作,则必须在程序的开头定义:  

extern bank1 unsigned char var1, var2;  //声明位于bank1的外部变量 


  volatile —  易变型变量声明。PICC 中还有一个变量修饰词在普通的C 语言介绍中一般是看不到的,它说明了一个变量的值是会随机变化的,即使程序没有刻意对它进行任何赋值操作。“volatile”类型定义在单片机的 C 语言编程中是如此的重要,是因为它可以告诉编译器的优化处理器这些变量是实实在在存在的,在优化过程中不能无故消除。


  假定你的程序定义了一个变量并对其作了一次赋值,但随后就再也没有对其进行任何读写操作,如果是非volatile 型变量,优化后的结果是这个变量将有可能被彻底删除以节约存储空间。另外一种情形是在使用某一个变量进行连续的运算操作时,这个变量的值将在第一次操作时被复制到中间临时变量中,如果它是非volatile型变量,则紧接其后的其它操作将有可能直接从临时变量中取数以提高运行效率,显然这样做后对于那些随机变化的参数就会出问题。只要将其定义成volatile 类型后,编译后的代码就可以保证每次操作时直接从变量地址处取数。


    const  —  常数型变量声明。这些变量就成为常数,程序运行过程中不能对其修改。除了位变量(这些位变量还是被放置在 RAM中,但程序不能对其赋值修改。),其它所有基本类型的变量或高级组合变量都将被存放在程序空间(ROM区)以节约数据存储空间。显然,被定义在ROM区的变量是不能再在程序中对其进行赋值修改的,这也是“const”的本来意义。


  persistent —  非初始化变量声明。按照标准C 语言的做法,程序在开始运行前首先要把所有定义的但没有预置初值的变量全部清零。PICC会在最后生成的机器码中加入一小段初始化代码来实现这一变量清零操作,且这一操作将在main 函数被调用之前执行。问题是作为一个单片机的控制系统有很多变量是不允许在程序复位后被清零的。为了达到这一目的,PICC 提供了“persistent ”修饰词以声明此类变量无需在复位时自动清零,编程员应该自己决定程序中的那些变量是必须声明成“persisten ”类型,而且须自己判断什么时候需要对其进行初始化赋值。


  例如: persistent unsigned char hour,minute,second;  //定义时分秒变量

5.PICC中的指针

 

   PICC中指针的基本概念和标准C 语法没有太多的差别。但是在 PIC 单片机这一特定的架构上,指针的定义方式还是有几点需要特别注意。

  1.指向RAM的指针

  如果是汇编语言编程,实现指针寻址的方法肯定就是用FSR 寄存器,PICC也不例外。这样就势必产生一个问题:FSR 能够直接连续寻址的范围是256 字节(bank0/1或bank2/3),要覆盖最大512 字节的内部数据存储空间,又该如何让定义指针?PICC还是将这一问题留给编程员自己解决:在定义指针时必须明确指定该指针所适用的寻址区域,例如:  


unsigned char *ptr0;    //①定义覆盖bank0/1的指针 

bank2 unsigned char *ptr1;  //②定义覆盖bank2/3的指针 

bank3 unsigned char *ptr2;  //③定义覆盖bank2/3的指针 

上面定义了三个指针变量,其中①指针没有任何bank 限定,缺省就是指向bank0 和bank1;②和③一个指明了bank2,另一个指明了bank3,

但实际上两者是一样的,因为一个指针可以同时覆盖两个bank 的存储区域。另外,上面三个指针变量自身都存放在 bank0 中。

我们将在稍后介绍如何在其它bank 中存放指针变量。


  既然定义的指针有明确的bank 适用区域,在对指针变量赋值时就必须实现类型匹配,同样的道理,若函数调用时用了指针作为传递参数,也必须注意bank 作用域的匹配,而这点往往容易被忽视。假定有下面的函数实现发送一个字符串的功能: 
          void SendMessage(unsigned char *); 
  那么被发送的字符串必须位于bank0 或bank1 中。如果你还要发送位于 bank2 或bank3 内的字符串,必须再另外单独写一个函数: 
          void SendMessage_2(bank2 unsigned char *); 
这两个函数从内部代码的实现来看可以一模一样,但传递的参数类型不同。 
  按笔者的应用经验体会,如果你看到了“Fixup overflow”的错误指示,几乎可以肯定是指针类型不匹配的赋值所至。请重点检查程序中有关指针的操作。

  2.指向ROM常数的指针 
  如果一组变量是已经被定义在ROM区的常数,那么指向它的指针可以这样定义: 
      const unsigned char company[]=”Microchip”;    //定义ROM 中的常数 
      const unsigned char *romPtr;     //定义指向ROM 的指针 
  程序中可以对上面的指针变量赋值和实现取数操作: 
      romPtr = company;  //指针赋初值 
      data = *romPtr++;  //取指针指向的一个数,然后指针加1 
  反过来,下面的操作将是一个错误,因为该指针指向的是常数型变量,不能赋值。 
      *romPtr  = data;  // 往指针指向的地址写一个数

  3.指向函数的指针

  单片机编程时函数指针的应用相对较少,但作为标准 C 语法的一部分,PICC同样支持函数指针调用。如果你对编译原理有一定的了解,就应该明白在PIC 单片机这一特定的架构上实现函数指针调用的效率是不高的:PICC 将在RAM中建立一个调用返回表,真正的调用和返回过程是靠直接修改PC指针来实现的。因此,除非特殊算法的需要,建议大家尽量不要使用函数指针。


㈠ bank 修饰词的位置含义 

前面介绍的一些指针有的作用于bank0/1,有的作用于 bank2/3,但它们本身的存放位置全部在bank0。显然,在一个程序设计中指针变量将有

可能被定位在任何可用的地址空间,这时,bank 修饰词出现的位置就是一个关键,看下面的例子: 

//定义指向bank0/1的指针,指针变量为于bank0中 

unsigned char *ptr0; 

//定义指向bank2/3的指针,指针变量为于bank0中 

bank2 unsigned char *ptr0;  

//定义指向bank2/3的指针,指针变量为于bank1中 

bank2 unsigned char * bank1 ptr0;  

从中可以看出规律:前面的 bank 修饰词指明了此指针的作用域;后面的 bank 修饰词定义了此指针变量自身的存放位置。只要掌握了这一法则,

你就可以定义任何作用域的指针且可以将指针变量放于任何bank 中。 

㈡ volatile、persistent 和const修饰词的位置含义 

如果能理解上面介绍的bank 修饰词的位置含义,实际上 volatile 、persistent 和const这些关键词出现在前后不同位置上的含义规律是和

bank 一词相一致的。例如: 

//定义指向bank0/1易变型字符变量的指针,指针变量位于bank0中且自身为非易变型 

volatile unsigned char *ptr0; 

//定义指向bank2/3非易变型字符变量的指针,指针变量位于bank1中且自身为易变型 

bank2 unsigned char * volatile bank1 ptr0; 

//定义指向ROM 区的指针,指针变量本身也是存放于ROM 区的常数 

const unsigned char * const ptr0; 

亦即出现在前面的修饰词其作用对象是指针所指处的变量;出现在后面的修饰词其作用对象就是指针变量自己。 


6.PICC 中的子程序和函数 
  中档系列的PIC 单片机程序空间有分页的概念,但用C 语言编程时基本不用太多关心代码的分页问题。因为所有函数或子程序调用时的页面设定(如果代码超过一个页面)都由编译器自动生成的指令实现。


  函数的代码长度限制,一个良好的程序设计应该有一个清晰的组织结构,把不同的功能用不同的函数实现是最好的方法,因此一个函数 2K字长的限制一般不会对程序代码的编写产生太多影响。如果为实现特定的功能确实要连续编写很长的程序,这时就必须把这些连续的代码拆分成若干函数,以保证每个函数最后编译出的代码不超过一个页面空间。


  调用层次的控制,中档系列PIC 单片机的硬件堆栈深度为8 级,考虑中断响应需占用一级堆栈,所有函数调用嵌套的最大深度不要超过7 级。编程员必须自己控制子程序调用时的嵌套深度以符合这一限制要求。


  中断函数的实现,PICC可以实现C 语言的中断服务程序。中断服务程序有一个特殊的定义方法: 
          void interrupt ISR(void); 
其中的函数名“ISR ”可以改成任意合法的字母或数字组合,但其入口参数和返回参数类型必须是“void ”型,亦即没有入口参数和返回参数,且中间必须有一个关键词“interrupt ”。中断函数可以被放置在原程序的任意位置。因为已有关键词“interrupt ”声明,PICC在最后进行代码连接时会自动将其定位到0x0004中断入口处,实现中断服务响应。编译器也会实现中断函数的返回指令“retfie”。


一个简单的中断服务示范函数如下: 

void  interrupt  ISR(void)  //中断服务程序 

   if (T0IE && T0IF)     //判TMR0 中断 

推荐阅读

史海拾趣

BTCPower公司的发展小趣事

随着全球环保意识的提高,绿色发展成为电子行业的重要趋势。BTCPower积极响应这一趋势,将绿色发展理念融入公司的产品和服务中。公司致力于研发更环保、更节能的产品,同时还推动供应链中的合作伙伴共同实现绿色生产。这些努力使得BTCPower在行业中树立了良好的环保形象。

Directed Energy Inc公司的发展小趣事

为了进一步扩大市场份额,Directed Energy Inc积极寻求与全球范围内的合作伙伴建立合作关系。通过与知名电子产品制造商、分销商和解决方案提供商的合作,公司成功将其产品推向了全球各地。这些合作伙伴不仅为Directed Energy Inc带来了更多的商业机会,还帮助公司更好地了解不同市场的需求和趋势,从而不断调整和优化产品策略。

Ericsson Power Modules公司的发展小趣事

在追求经济效益的同时,Ericsson Power Modules也注重环保和可持续发展。公司积极采用环保材料和绿色生产工艺,降低生产过程中的能耗和排放。此外,Ericsson Power Modules还积极参与环保公益活动,推动电子行业的绿色发展。这种环保意识和行动体现了Ericsson Power Modules对社会的责任感和对可持续发展的承诺。

GuangDong Province MengCo Semiconductor Co., Ltd公司的发展小趣事
对于易发热的元器件(如制动电阻),应加强散热设计,确保其在工作过程中能够正常散热。
粤翔(FlyWin)公司的发展小趣事
如DC-DC升压/降压电路,用于在电压异常时调整电压输出,确保关键部件仍能正常工作。
ELM [ELM Electronics]公司的发展小趣事

ELM始终将品质管理作为公司发展的核心。公司建立了完善的质量管理体系,从原材料采购到生产、检测、包装等各个环节都进行严格的质量控制。同时,ELM还注重客户需求和反馈,不断优化产品和服务,以满足客户的个性化需求。这些举措使得ELM的产品在市场上享有很高的声誉和口碑,客户满意度持续提升。

问答坊 | AI 解惑

这是一个客户对T8 T10的驱动的要求-能做到吗?

现在的电源很多;对于一个刚做电源的人来说,IC选型也是至关重要的一环;看大伙   心中是否已经有合适的方案选择,说来听听;我觉得要是过安规 做隔离的电源效率很   难做到95%以上,这种要求确实很苛刻的;不知道有没有这么好的电 ...…

查看全部问答>

问个near,far,huge指针的问题~~~

目前维护一个工控机用的dos程序,发现老代码似乎有问题,是这样的 long huge* pdata; // 声明巨指针 pdata=(long huge*)farcalloc(1024,sizeof(long)); //分配空间,能容纳1024个long 然后data作为某个函数的实参这样调用: ReadAndAdd(1, pdata) ...…

查看全部问答>

请教:基于嵌入式系统的PC键盘与鼠标交互控制模拟研究(大学课设)

   正如题如示,我们的毕业设计要求做一个关于:PC键盘与鼠标交互控制模拟研究的实验。任务描述如下:    随着信息技术发展,各类应用软件不断丰富。已有的商用软件的高效率切入成为一个十分棘手的问题。如一款基于PC的性能完 ...…

查看全部问答>

VHDL让CPLD的某个输入信号延时10微秒输出

CDLD时钟是25MHZ,我想用减法计数器实现,是不是计数器减到0时,直接输出信号《=输入信号就行了?…

查看全部问答>

菜鸟问题::wince下如何实现对话框全屏!

以前的帖子我也看过我拿MSDN无法查到SHFullScreen这个函数!求各位大侠指点一下!!…

查看全部问答>

寻找一篇关于UC/OS-II的方面的英文文献,加分了!!!

有那位仁兄能给我一个关于UC/OS-II的英文文献(其任何一方面的知识,字数3000左右),最好有中英文对照的!正确的网址也行! 谢谢! …

查看全部问答>

dsp pwm控制电机

遇到一个问题,大家帮忙解决一下。 我用dsp2812的pwm控制电机,还加上液晶显示,测速,无线等功能,不接电机驱动板时,所有功能都是好的,但一接上电机驱动板,所有功能就不能用了,只能停在程序运行的当前状态。…

查看全部问答>

路在何方?

我大三了,现在有点迷茫了!学得是自动化专业,学校开得课很感觉很杂,强电,弱电,硬件,软件,我们都开了,,但多而不精。自己又不打算考研了,真不知道自己毕业之后从事什么工作。出去具体是从事哪些事?是强电好,还是弱电好?很迷茫啊!…

查看全部问答>

【低功耗】Low-power high-level synthesis for FPGA architectures

This paper addresses two aspects of low-power design for FPGA circuits. First, we present an RT-level power estimator for FPGAs with consideration of wire length. The power estimator closely reflects both dynamic and static power ...…

查看全部问答>

手控电脑屏~~自己做的哦

$(\'swf_N1n\').innerHTML=AC_FL_RunContent(\'width\', \'550\', \'height\', \'400\', \'allowNetworking\', \'internal\', \'allowScriptAccess\', \'never\', \'src\', encodeURI(\'http://player.youku.com/player.php/sid/XMzU2MjI0ODgw/v.sw ...…

查看全部问答>