热度 24
2012-12-16 10:56
4269 次阅读|
9 个评论
单片机工程师面对一种新单片机时,最希望的是能有一个简单的样例,这个样例连上仿真器就能运行,里面最好包含一些基本功能,这样工程师就可以在这个样例的基础上很快改出自己需要的代码。 这里我以应广pdk22c12写了一段程序框架,已经包含对这个单片机的各种基本设置,拿回去就可以自己进行仿真调试,相信能让新接触应广单片机的朋友很快上手。 //----------------------------------------- //应广单片机软件基本框架例程 //本例仅供参考,欢迎指正程序中的问题 //本例是根据应广单片机的特点创建的基本程序框架 //包含定时中断、外部中断、AD转换、段位数码管显示,简单按键处理等功能 //用户在本例基础上很容易就能改出自己需要的程序 //2012年12月15日 // //作者:戴上举 //邮箱:daishangju@163.com //博客:forum.eet-cn.com/BLOG_daishangju_334.HTM //电话:13509678051 //Q Q:1514292225 //----------------------------------------- .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 已编译,未调试