应广单片机系列——基本应用程序框架

描述

注:本文是作者以前发表在其个人博客,现在发布到电子发烧友专栏


单片机工程师面对一种新单片机时,最希望的是能有一个简单的样例,这个样例连上仿真器就能运行,里面最好包含一些基本功能,这样工程师就可以在这个样例的基础上很快改出自己需要的代码。

 

这里我以应广pdk22c12写了一段程序框架,已经包含对这个单片机的各种基本设置,拿回去就可以自己进行仿真调试,相信能让新接触应广单片机的朋友很快上手。

 

//-----------------------------------------
//应广单片机软件基本框架例程
//本例仅供参考,欢迎指正程序中的问题
//本例是根据应广单片机的特点创建的基本程序框架
//包含定时中断、外部中断、AD转换、段位数码管显示,简单按键处理等功能
//用户在本例基础上很容易就能改出自己需要的程序

.chip pdk22c12
//{{PADAUK_CODE_OPTION
 .Code_Option LVD  2.4V~2.9V // Maximum performance = 8 MIPS
 .Code_Option Security Enable  // Security 7/8 words Enable
//}}PADAUK_CODE_OPTION
//#define MOB_FLASH_MODE

KEY equ pa.5

//定义数码管的IO口,这里是显示三个8
LED_A equ pa.1
LED_B equ pa.0
LED_C equ pa.7
LED_D equ pa.6
LED_E equ pb.7
LED_F equ pb.6
LED_G equ pb.5
LED_DP equ pb.1
LED_COM1 equ pa.2
LED_COM2 equ pa.3
LED_COM3 equ pa.4
LED_A_ON equ set1 LED_A
LED_A_OFF equ set0 LED_A
LED_B_ON equ set1 LED_B
LED_B_OFF equ set0 LED_B
LED_C_ON equ set1 LED_C
LED_C_OFF equ set0 LED_C
LED_D_ON equ set1 LED_D
LED_D_OFF equ set0 LED_D
LED_E_ON equ set1 LED_E
LED_E_OFF equ set0 LED_E
LED_F_ON equ set1 LED_F
LED_F_OFF equ set0 LED_F
LED_G_ON equ set1 LED_G
LED_G_OFF equ set0 LED_G
LED_DP_ON equ set1 LED_DP
LED_DP_OFF equ set0 LED_DP
SELECT_LED1 macro
 set1 LED_COM2
 set1 LED_COM3
 set0 LED_COM1
 endm
SELECT_LED2 macro
 set1 LED_COM1
 set1 LED_COM3
 set0 LED_COM2
 endm
SELECT_LED3 macro
 set1 LED_COM1
 set1 LED_COM2
 set0 LED_COM3
 endm
ALL_LED_OFF macro
 set1 LED_COM1
 set1 LED_COM2
 set1 LED_COM3
 LED_A_OFF
 LED_B_OFF
 LED_C_OFF
 LED_D_OFF
 LED_E_OFF
 LED_F_OFF
 LED_G_OFF
 LED_DP_OFF
 endm

LED_DELAY macro
 delay 250
 delay 250
 endm

word init_timer
//用于数码管显示时进行查表转换
word disp_ptr
word disp_data
word disp_data_temp

byte Xms
byte ms_cnt
byte pb2_voltage

//用于数码管显示
byte disp1_buf
byte disp2_buf
byte disp3_buf
byte disp_temp
byte led1_buf
byte led2_buf
byte led3_buf

//用于定时中断计时
byte timer_cnt
//用于单键按键判断
byte key_cnt
bit key_press_flag

//定义标志位,用于数码管显示和闪烁控制
bit led_en_flag
bit led_flash_flag
bit update_disp_flag

//应广单片机程序入口,第一条必须为跳转到第一个内核主程序入口地址的指令,第二条为第二个内核,有几个内核就有几条
.romadr 0x000
 goto main0
 goto main1


//应广单片机中断程序入口地址,所有中断共用同一个入口,需要用户自己判断中断类型
.romadr 0x010
 pushaf
 if(intrq.T16) //定时中断
 {
  stt16 init_timer //重设定时器值
  if(timer_cnt < 9) //得到1000ms间隔
  {
   timer_cnt ++
  }
  else
  {
   timer_cnt = 0
   if(led_flash_flag) //数码管闪烁处理
   {
    led_flash_flag = 0
   }
   else
   {
    led_flash_flag = 1
   }
  }
  intrq.T16 = 0
 }
 elseif(intrq.PB0) //PB0外部中断
 {
  if(pb.0)
  {
   //读到PB0状态为高,为上升沿
   nop //添加用户自己的代码
  }
  else
  {
   //读到PB0状态为低,为下降沿
   nop //添加用户自己的代码
  }
 }
 intrq.AD = 0 //强制清除AD中断标志位,防止意外进入AD中断后程序不停响应
 intrq.PA0 = 0 //强制清除PA0外部中断标志位,防止意外进入PA0中断后程序不停响应
 popaf
 reti

//----------------------------------------
//input: ms
//用该函数可以再4M的频率下得到近似1毫秒的延时,在第一个内核中调用中断会导致延时加长
//----------------------------------------
delayXms:
 while(Xms)
 {
  wdreset //这里需要有清看门狗操作,否则有可能在长延时下导致看门狗溢出复位
  ms_cnt = 20
  while(ms_cnt)
  {
   delay 195
   ms_cnt--
  }
  Xms--
 }
 ret


//----------------------------------------
//
//对PB2进行AD转换,得到上面的电压
//----------------------------------------
get_pb2_voltage:
 //对新的一路AD通道进行AD转换时,第一次转换的结果可能不可靠,这里连续转换两次,取第二次结果
 //如果连续对同一通道进行AD转换,可以只转换一次
 adcc = 0b10_0010_00 //enable ADC, select pb2 
 ad_start = 1
 wait1 ad_start //等待AD转换结束
 a = adcr //放弃第一次转换结果
 ad_start = 1
 wait1 ad_start
 pb2_voltage = adcr //存储第二次转换结果
 ret

//数码管BCD显示用的转换表,最后的两个0x00可以不要
//数码管的a,b,...,g,dp分别对应bit7,bit6,...,bit0
bcd_tbl: //0~9
 dc 0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xF6,0x00,0x00
//----------------------------------------
//以十进制形式显示数据disp_data
//只修改显示缓冲区
//----------------------------------------
update_led_disp_buf:
 //a,b,...,g,dp --> bit7,bit6,...,bit0
 if(!update_disp_flag)
 {
  disp_data_temp = disp_data //先将需要显示的数据放到临时中间变量中,防止转换时数据更新导致显示出错

  //得到数据管第一位LED1的BCD码
  disp_temp = 0
  while(disp_data_temp >= 100) //直接用循环减实现除法
  {
   disp_data_temp = disp_data_temp - 100
   disp_temp ++
  }
  disp_ptr = bcd_tbl //查表操作
  disp_ptr = disp_ptr + disp_temp
  ldtabl disp_ptr
  mov disp1_buf,a
  //得到数据管第二位LED2的BCD码
  disp_temp = 0
  while(disp_data_temp >= 10)
  {
   disp_data_temp = disp_data_temp - 10
   disp_temp ++
  }
  disp_ptr = bcd_tbl
  disp_ptr = disp_ptr + disp_temp
  ldtabl disp_ptr
  mov disp2_buf,a
  //得到数据管第三位LED3的BCD码
  disp_temp = disp_data_temp
  disp_ptr = bcd_tbl
  disp_ptr = disp_ptr + disp_temp
  ldtabl disp_ptr
  mov disp3_buf,a

  update_disp_flag = 1
 }

 ret

//第一个内核程序入口
//----------------FPPA0-------------------
main0:

 .ADJUST_OTP_IHRCR 8MIPS  // IHRC/2 = 8MIPS, WatchDog Disable, RAM 0,1 temporary be used

 sp = 0x30 //设置第一个内核的堆栈地址

 //禁止中断和定时器
 disgint
 inten = 0
 mov a,0b000_11_111 //disable timer
 mov t16m,a

 //小延时后在修改其它系统状态设置
 delay 200
 clkmd.1 = 1 //打开看门狗,这个设置尽量靠前,以增强可靠性
 wdreset //清看门狗

 //设置IO口
 pac = 0b1101_1111 //PA5设置 IN
 paph = 0b0000_0000
 pbc = 0b1111_1010 //PB2设为模拟输入不开上拉电阻,PB0设为输入
 pbph = 0b0000_0000 //poll high

 ALL_LED_OFF

 init_timer = 7768 //从7768进行校准为100ms
 mov a,0b100_11_111
 mov t16m,a
 stt16 init_timer

 //上电后清需要使用的变量
 key_cnt = 0
 disp1_buf = 0
 disp2_buf = 0
 disp3_buf = 0
 led1_buf = 0
 led2_buf = 0
 led3_buf = 0
 update_disp_flag = 0
 timer_cnt = 0
 disp_data = 000
 led_en_flag = 1 //数码管进行显示

 //将PB2设为模拟输入口进行AD转换
 adcdi = 0b0000_0100 //pb2 is analog input
 adcc = 0b10_0010_00 //enable ADC, select pb2
 adcm = 0b000_0100_0 //system clock/16
 //adcm = 0b000_0111_0 //system clock/128

 

 //延时一段时间等系统稳定
 Xms = 100
 call delayXms

 //得到按键初始状态,这样在按键损坏时不会误判按键按下或松开
 if(!KEY)
 {
  key_press_flag = 1
 }
 else
 {
  key_press_flag = 0
 }


 stt16 init_timer
 intrq = 0
 inten.T16 = 1 //打开定时中断
 inten.PB0 = 1 //打开PB0外部中断
 engint //允许中断
 set1 fppen.1 //打开第二个内核

 

main0_loop:    

 wdreset //clear watch dog

 //得到PB2的AD转换结果
 call get_pb2_voltage
 //AD转换完立即更新数码管显示缓冲区
 call update_led_disp_buf

 if(!KEY) //电压恢复正常只要按键就立刻结束倒计时
 {
  if(key_cnt < 3)
  {
   key_cnt ++
  }
  else
  {
   if(!key_press_flag)
   {
    key_press_flag = 1 //这里是按键按下
    //按键切换数码管是否进行显示
    if(led_en_flag)
    {
     led_en_flag = 0 //数码管不显示
    }
    else
    {
     led_en_flag = 1 //数码管显示
    }
   }
  }
 }
 else
 {
  if(key_cnt)
  {
   key_cnt --
  }
  else
  {
   if(key_press_flag)
   {
    key_press_flag = 0 //这里是按键松开
   }
  }
 }

 //延时50毫秒,目的是让第一个内核循环的时间大于第二个内核循环时间的两倍
 //以保证显示缓冲区再次更新前第二个核已经做出响应,保证显示正确
 Xms = 50
 call delayXms

 goto main0_loop

//第二个内核程序入口
//----------------FPPA1-------------------
main1:
 sp = 0x38 //设置第二个内核的堆栈地址
 delay 200
main1_loop:
 if(update_disp_flag) //有数据更新时才进行更新
 {
  led1_buf = disp1_buf
  led2_buf = disp2_buf
  led3_buf = disp3_buf
  update_disp_flag = 0
 }

 //第二个内核循环扫描显示数码管,这样可以得到没有闪烁的显示效果
 if(led_en_flag) //数码管需要显示
 {
  //下面程序尽量让数码管每个段位的处理时间相同,这样可以保证各个段位亮度一致
  //LED1
  ALL_LED_OFF
  LED_DELAY
  SELECT_LED1
  if(led1_buf.7)
  {
   LED_A_ON
  }
  LED_DELAY
  LED_A_OFF
  if(led1_buf.6)
  {
   LED_B_ON
  }
  LED_DELAY
  LED_B_OFF
  if(led1_buf.5)
  {
   LED_C_ON
  }
  LED_DELAY
  LED_C_OFF
  if(led1_buf.4)
  {
   LED_D_ON
  }
  LED_DELAY
  LED_D_OFF
  if(led1_buf.3)
  {
   LED_E_ON
  }
  LED_DELAY
  LED_E_OFF
  if(led1_buf.2)
  {
   LED_F_ON
  }
  LED_DELAY
  LED_F_OFF
  if(led1_buf.1)
  {
   LED_G_ON
  }
  LED_DELAY
  LED_G_OFF
  if(led1_buf.0)
  {
   LED_DP_ON
  }
  LED_DELAY
  LED_DP_OFF
  //LED2
  ALL_LED_OFF
  LED_DELAY
  SELECT_LED2
  if(led2_buf.7)
  {
   LED_A_ON
  }
  LED_DELAY
  LED_A_OFF
  if(led2_buf.6)
  {
   LED_B_ON
  }
  LED_DELAY
  LED_B_OFF
  if(led2_buf.5)
  {
   LED_C_ON
  }
  LED_DELAY
  LED_C_OFF
  if(led2_buf.4)
  {
   LED_D_ON
  }
  LED_DELAY
  LED_D_OFF
  if(led2_buf.3)
  {
   LED_E_ON
  }
  LED_DELAY
  LED_E_OFF
  if(led2_buf.2)
  {
   LED_F_ON
  }
  LED_DELAY
  LED_F_OFF
  if(led2_buf.1)
  {
   LED_G_ON
  }
  LED_DELAY
  LED_G_OFF
  if(led2_buf.0)
  {
   LED_DP_ON
  }
  LED_DELAY
  LED_DP_OFF
  //LED3
  ALL_LED_OFF
  LED_DELAY
  SELECT_LED3
  if(led3_buf.7)
  {
   LED_A_ON
  }
  LED_DELAY
  LED_A_OFF
  if(led3_buf.6)
  {
   LED_B_ON
  }
  LED_DELAY
  LED_B_OFF
  if(led3_buf.5)
  {
   LED_C_ON
  }
  LED_DELAY
  LED_C_OFF
  if(led3_buf.4)
  {
   LED_D_ON
  }
  LED_DELAY
  LED_D_OFF
  if(led3_buf.3)
  {
   LED_E_ON
  }
  LED_DELAY
  LED_E_OFF
  if(led3_buf.2)
  {
   LED_F_ON
  }
  LED_DELAY
  LED_F_OFF
  if(led3_buf.1)
  {
   LED_G_ON
  }
  LED_DELAY
  LED_G_OFF
  if(led3_buf.0)
  {
   LED_DP_ON
  }
  LED_DELAY
  LED_DP_OFF
 }
 else //数码管不需要显示
 {
  ALL_LED_OFF
 }

 goto main1_loop

   

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分