发新帖我要提问
12
返回列表
[其他产品]

PIC单片机C语言简记

[复制链接]
楼主: comparison
手机看帖
扫描二维码
随时随地手机跟帖
comparison|  楼主 | 2020-8-13 10:08 | 显示全部楼层
例如:  

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 中存放指针变量。

使用特权

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

使用特权

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

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:10 | 显示全部楼层
 3.指向函数的指针

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

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:11 | 显示全部楼层
㈠ 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 中。

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:12 | 显示全部楼层
㈡ 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;
亦即出现在前面的修饰词其作用对象是指针所指处的变量;出现在后面的修饰词其作用对象就是指针变量自己。

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:13 | 显示全部楼层
6.PICC 中的子程序和函数
  中档系列的PIC 单片机程序空间有分页的概念,但用C 语言编程时基本不用太多关心代码的分页问题。因为所有函数或子程序调用时的页面设定(如果代码超过一个页面)都由编译器自动生成的指令实现。

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

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

使用特权

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

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:14 | 显示全部楼层
一个简单的中断服务示范函数如下:
void  interrupt  ISR(void)  //中断服务程序
{
   if (T0IE && T0IF)     //判TMR0 中断
   {
T0IF = 0;    // 清除TMR0 中断标志
      //在此加入TMR0 中断服务
   }
   if (TMR1IE && TMR1IF)  //判TMR1 中断
   {
  TMR1IF = 0;   //清除TMR1 中断标志
      //在此加入TMR1 中断服务
   }
}      //中断结束并返回

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:15 | 显示全部楼层
PICC会自动加入代码实现中断现场的保护,并在中断结束时自动恢复现场,所以编程员无需象编写汇编程序那样加入中断现场保护和恢复的额外指令语句。但如果在中断服务程序中需要修改某些全局变量时,是否需要保护这些变量的初值将由编程员自己决定和实施。用C 语言编写中断服务程序必须遵循高效的原则:

  1.代码尽量简短,中断服务强调的是一个“快”字。2.避免在中断内使用函数调用。3.避免在中断内进行数**算。

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:15 | 显示全部楼层
 标准库函数,PICC提供了较完整的C 标准库函数支持,其中包括数**算函数和字符串操作函数。前加“#include <math.h> ”  包含头文件,“#include <string.h> ”头文件。

  PICC 定义特殊区域值:

  1  定义工作配置字,在用 PICC写程序时同样可以在C 原程序中定义,具体方式如下:
            __CONFIG (HS & UNPROTECT & PWRTEN & BORDIS & WDTEN);

 上面的关键词“__CONFIG ”(注意前面有两个下划线符)专门用于是芯片配置字的设定,后面括号中的各项配置位符号在特定型号单片机的头文件中已经定义(注意不是pic.h头文件),相互之间用逻辑“与”操作符组合在一起。这样定义的配置字信息最后将和程序代码一起放入同一个HEX文件。

使用特权

评论回复
a511484095| | 2020-8-13 10:15 | 显示全部楼层
为什么不用XC8/12/16···呢?

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:16 | 显示全部楼层
在这里列出了适用于16F7x 系列单片机配置位符号预定义,其它型号或系列的单片机配置字定义方式类似,使用前查阅一下对应的头文件即可。
/*振荡器配置*/
#define RC    0x3FFF  // RC 振荡
#define HS    0x3FFE  // HS 模式
#define XT    0x3FFD  // XT 模式
#define LP    0x3FFC  // LP 模式

/*看门狗配置*/
#define WDTEN   0x3FFF  // 看门狗打开
#define WDTDIS    0x3FFB  // 看门狗关闭
/*上电延时定时器配置*/
#define PWRTEN    0x3FF7  // 上电延时定时器打开
#define PWRTDIS  0x3FFF  // 上电延时定时器关闭

/*低电压复位配置*/
#define BOREN   0x3FFF  // 低电压复位允许
#define BORDIS    0x3FBF  // 低电压复位禁止

/*代码保护配置*/
#define UNPROTECT  0x3FFF  // 没有代码保护
#define PROTECT  0x3FEF  // 程序代码保护

   头文件预定义的配置信息符号

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:17 | 显示全部楼层
定义芯片标记单元
  PIC 单片机中的标记单元定义可以用下面的__IDLOC(注意前面有两个下划线符)预处理指令实现,方法如下:
    __IDLOC (1234);
  其特殊之处是括号内的值全部为16进制数,不需要用“0x”引导。这样上面的定义就设定了标记单元内容为01020304 。

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:18 | 显示全部楼层
C 和汇编混合编程

  在C 原程序中直接嵌入汇编指令是最直接最容易的方法。如果只需要嵌入少量几条的汇编指令,PICC提供了一个类似于函数的语句:
                  asm(“clrwdt”);
双引号中可以编写任何一条PIC 的标准汇编指令。例如:

for (;;) {
   asm("clrwdt");    //清看门狗
   Task();
   ClockRun();
   asm("sleep");    //休眠
   asm("nop");   //空操作延时
}

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:19 | 显示全部楼层
 如果需要编写一段连续的汇编指令,PICC支持另外一种语法描述:用“#asm”开始汇编指令段,用“#endasm ”结束。一句话:用了C 语言后,就不要再老想着用汇编。尽量使用全局变量进行参数传递。

  类似于纯汇编文件的代码也可以在C 语言框架下实现,方法是基于C 标准语法定义所有的变量和函数名,包括需要传递的形式参数、返回参数和局部变量,但函数内部的指令基本用嵌入汇编指令编写,只有最后的返回参数用C 语句实现。这样做后函数的运行效率和纯汇编编写时几乎一模一样,但各参数的传递统一用C 标准实现,这样管理和维护就比较方便。例如下面的实现一个字节变量的偶校验位计算。

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:19 | 显示全部楼层
bit EvenParity(unsigned char data)
{
    #asm
    swapf   ?a_EvenParity+0,w   //入口参数data 的寻址符为 ?a_EvenParity+0
    xorwf   ?a_EvenParity+0,f
    rrf     ?a_EvenParity+0,w
    xorwf   ?a_EvenParity+0,f
    btfsc   ?a_EvenParity+0,2
    incf    ?a_EvenParity+0,f
    #endasm
    //至此,data 的最低位即为偶校验位
    if (data&0x01) return(1);
    else return(0);
}

使用特权

评论回复
comparison|  楼主 | 2020-8-13 10:20 | 显示全部楼层

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则