找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2939|回复: 2
收起左侧

basic语言与汇编语言混合编程---电子手表

[复制链接]
ID:342822 发表于 2020-8-22 09:50 | 显示全部楼层 |阅读模式
本帖最后由 taotie 于 2020-8-22 10:17 编辑

屏幕截图(38).png

这是一个外国网站的程序,在bascom avr平台采用basic语言与汇编语言混合编程。下载回来对其进行部分改造:
去掉lcd16x4液晶改用流行的oled屏仿真。
增加中文设置提示。
注意:源程序是用Bascom-Avr-1.11.9.1编写的,在新版Bascom-Avr须在非汇编块中的汇编指令前增加“!”字符



$regfile = "m8def.dat"
$framesize = 32
$swstack = 32
$hwstack = 32
$crystal = 8000000

Config Portb.1 = Output                                     '一个引脚配置为LED输出

'********* LCD ***************************************************
''设置LCD模块的针脚,必要时根据您自己的连接
' Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0
'Config Lcdmode = Port
'Config Lcdbus = 4                                          '4 bit mode
' Config Lcd = 20 * 4
' Initlcd
'Cursor Off
Config Scl = Portc.5                                                           ' 用I2C引脚Scl = Portc.5 ,Sda = Portc.4
Config Sda = Portc.4
Config Twi = 400000                                                             ' i2c 的速度

I2cinit
$lib "i2c_twi.lbx"                                                                        ' 不使用模拟I2c的软件,而是使用twi
$lib "glcdSSD1306-I2C.lib"                                                        ' 用glcdSSD1306-I2C库替换默认库要添加在bascom avr库中

#if _build < 20784
Dim ___lcdrow As Byte , ___lcdcol As Byte                                ' 老版本变量格式进行编译
#endif

Config Graphlcd = Custom , Cols = 128 , Rows = 64 , Lcdname = "SSD1306"   '配置图形显示器


'********* ISR Timer 2 ***************************************************
Config Clock = User      '配置用于Time$和Date$变量的计时器。

'您可以以128Hz的频率去抖动(最大延迟4/128 = 0.031s)
Config Timer2 = Timer , Async = On , Prescale = 1         '预分频器8->16Hz或1->128Hz
On Ovf2 _isr_t2ovf Nosave
Enable Ovf2

Enable Interrupts

Config Date = ymd , Separator = -        '配置日期格式    欧洲:dmy    美国:mdy       亚洲:ymd
_sec = 00 : _min = 15 : _hour = 14 : _day = 21 : _month = 08 : _year = 20

Dim Flagrtc_changed As Bit
Dim Tick_128hz As Byte

'********* 输入键和去抖***************************************************
'将按钮分配给引脚-它们必须全部连接到同一端口!
Const Key_plus = &B0000_1000                                '1 =按钮输入
Const Key_minus = &B0001_0000
Const Key_enter = &B0010_0000

Const Pinb_mask = Key_plus + Key_minus + Key_enter         '过滤输入的掩码
Const Pinb_mask_compl = &HFF - Pinb_mask                    'Pinb_mask compliment

'这是PortB上的按钮
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT
Portb = Portb Or Pinb_mask                                  '1 =激活输入的内部上拉电阻
Key_port Alias Pinb

Dim Key_state As Byte                                      '0/1按钮的反转状态(1 =按下)
Dim Key_press As Byte                                     '触发按键
Dim Key_rep As Byte                                        '自动重复计数器
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 位计数器用于反跳

Const Key_repeat_start = 127                               '计时器ISR的编号-1,直到自动重复开始(大约500-1000ms)
Const Key_repeat_next = 15                                '定时器ISR的编号-1,用于自动重复重复率(大约125毫秒)

'反跳初始值
Key_ct0 = 255                                             '拦截程序从按键开始
Key_ct1 = 255
'Key_rep = Key_repeat_start                                 'Paranoia

'********* 一般的 ***************************************************
Dim I As Byte , J As Byte , K As Byte , L As Byte
Dim q As Byte
Dim Setdatetime_index As Byte
Setdatetime_index = 2                                       '日期数组的第一个索引为1,2 = _min

Dim Date_array(6) As Byte At _sec Overlay                   '秒/分钟/小时/天/月/年的数组指定在内存中覆盖变量。

'********* 主循环 ***************************************************
Do

'  Home                                                      '光标原点
' Lcd Date$ ; "  " ; Time$                                  '显示日期和时间
'  Locate 2 , 1
' Lcd "Set: " ; Lookupstr(setdatetime_index , Data_dateset)  'var = LOOKUPSTR(索引,标签)从表中返回一个字符串
Setfont font8x8
Lcdat 2, 16 , Date$
Setfont font12x16
Lcdat 4, 16 ,Time$
Setfont font8x8
'Lcdat 7, 16 , "Set: " ; Lookupstr(setdatetime_index , Data_dateset)  'LOOKUPSTR(索引,标签)从表中返回一个字符串
'为了节省电源,在ASM中编写了128Hz环路:
Setfont font12x16
q=Lookup(setdatetime_index , Data_set)
Lcdat 7, 16 ,chr(134);chr(135);  ": " ; chr(q)

   Flagrtc_changed = 0
   Enable Interrupts                                        'Paranoia
   '进行节电循环直到((Flagrtc_changed = 1)或(Key_press> 0))

Main_1:

Config Powermode = Powersave                    '配置电源模式=省电模式  如果定时器/计数器2启用,它将在睡眠期间保持运行
                                                                    '省电模式仅适用于8535、Mega8、Mega163。
!   lds     r16,{Key_press}                                  '按下按钮了吗?
  !  TST     r16
  !  BRNE    main_2
  !  lds     r16,{flagRTC_Changed}                            '再来一秒钟?
!   sbrS    r16,bit.flagRTC_Changed
!   RJMP    Main_1

Main_2:

If Key_press > 0 Then Gosub Setdatetime_input

Loop


'---------------------------------------------------------------
'子例程:Setdatetime_input         设置日期时间输入

'呼叫来源:main

'目的:设定日期和时钟

'参数:键输入
'---------------------------------------------------------------
Setdatetime_input:

Disable Interrupts                                         '禁用中断
    I = Key_press                                           '读取键
    Key_press = 0

    L = Lookup(setdatetime_index , Date_max)
    If Setdatetime_index = 4 Then
       L = Lookup(_month , Date_month)
       K = _year And 3
       If _month = 2 Then If K = 0 Then Incr L
    End If
    K = Lookup(setdatetime_index , Date_min)

    J = Date_array(setdatetime_index)
    If I = Key_plus Then
        If J < L Then Incr J Else J = K
    End If
    If I = Key_minus Then
        If J > K Then Decr J Else J = L
    End If
    Date_array(setdatetime_index) = J

Enable Interrupts                                          '启用中断

    If I = Key_enter Then
        If Setdatetime_index < 6 Then Incr Setdatetime_index Else Setdatetime_index = 1
    End If

Return


'********ISR计时器2***************************************************

'---------------------------------------------------------------

'子程序:'u isr_t2ovf

'呼叫来源:定时器2(128Hz)

'目的:RTC,去抖动

'结果:RTC:_秒、_min、\u小时、\u天、\u月、\u年
'          '反跳:请参见下文
'---------------------------------------------------------------
$external _soft_clock
Const _sectic = 0                                          'CONFIG CLOCK = USER,GOSUB <> SECTIC的编译器语句

_isr_t2ovf:
$asm
        push    r16
        in      r16,sreg
        push    r16                                        ' 将R16和SREG保存在堆栈中

' 防抖键
        push    r17                                         '现在保存所有使用的寄存器
        push    r18
        PUSH    r19
       Call Debounce_port
        POP     r19                                        '恢复寄存器
        pop     r18
        pop     r17

        lds     r16,{tick_128hz}                            '每1/128秒增加一次tick_128
        inc     r16
        sts     {tick_128hz},r16
        andi    r16,128-1                                   '如果_tick128不是128的倍数
        brne    _T2OVF_END                                  '   set_digit退出isr,否则已过去一秒钟

'  该位变量将每隔一秒钟设置一次
        lds     r16,{flagRTC_Changed}                       '位变量
        sbr     r16,2^bit.flagRTC_Changed
        sts     {flagRTC_Changed},r16                       '注意标志是重置标志的主要代码责任

'  转至内部Bascom ISR例行程序:软时钟
        pop     r16                                         '获取SREG的内容
        !out    sreg,r16                                    '恢复sreg
        pop     r16
        JMP    _SOFT_CLOCK                                  '原始RETI

_t2ovf_end:
        pop     r16                                         '获取SREG的内容
        !out    sreg,r16                                    '恢复SREG
        pop     r16

$end Asm
Return                                                      '带着RETI离开isr

'*********防抖******************************************* ************
'------------------------------------------------- --------------
'子程序:Debounce_port
''同时检测最大 8个按键,具有反跳和自动重复功能
'每个键被扫描3次以更改状态
'使用的寄存器:R16,R17,R18,R19
'变量:键0/1的Key_state状态
'Key_rep自动重复的重复计数器
'Key_ct0 / Key_ct1 2位计数器,用于反跳
'结果:Key_press:检测到按下键时,引脚位变为1
'读出后在Main中重置Key_press的状态!
'感谢彼得·丹纳格(Peter Dannegger)
'---------------------------------------------------------------
Debounce_port:                                              '大约108个字节
  $asm
    in R18, key_port
    com R18                                                 '引脚低电平有效
    ANDI R18, Pinb_mask                                     '带Pinb_mask的过滤器引脚
    LDS R19, {Key_state}
    eor R18, R19                                           '在引脚输入处检测电平变化

    LDS R16, {Key_ct0}                                     '8通道2位计数器
    LDS R17, {Key_ct1}
    and R16, R18                                           '重置R17中的2位计数器:R16
    and R17, R18
    com R16                                                 '递减R17:R16
    eor R17, R16
    STS {Key_ct0} , R16                                    '保存计数器
    STS {Key_ct1} , R17

    and R18, R16                                          'IF counter=&B11设置数字输入消隐
    and R18, R17
    eor R19, R18                                            '0<->1切换状态
    STS {Key_state} , R19

    LDS R16, {key_press}
    and R18, R19
    or R16, R18                                            '检测到“0->1按键

    LDS R17, {Key_rep}
    tst R19                                              'Key_state:是否按下任何键?
    breq Key_isr_rep
    dec R17
    brpl Key_isr_finish                                    'IF Key_rep <0 set_digit等待时间到期
    mov R16, R19                                            '自动重复key_press = key_state
    ldi R17, key_repeat_next                              '设置自动回复重复计时器
    rjmp Key_isr_finish

Key_isr_rep:
    ldi R17, key_repeat_start                             '重置自动回复开始计时器

Key_isr_finish:
    STS {Key_rep} , R17                                     '保存自动回复计时器
    STS {key_press} , R16                                  '保存去抖键
  $end Asm
Return


'********* RTC ***************************************************
'如果要使用以下空例程
'时间/日期bascom功能。 您可以根据需要填充它们
Getdatetime:
    Return
Settime:
    Return
Setdate:
    Return
End

'******** 选择日期和时间 ************************************
Date_min:
Data 0 , 0 , 0 , 0 , 1 , 1 , 0        '‘秒”,“分钟”,“小时”,“日”,“月”,“年”最小标志
Date_max:
Data 0 , 59 , 59 , 23 , 31 , 12 , 99  '‘秒”,“分钟”,“小时”,“日”,“月”,“年”最大标志
Date_month:
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 '12个月月末标志
Data_dateset:
Data " " , "Sec " , "Min " , "Hour" , "Day " , "Mon " , "Year"  '秒”,“分钟”,“小时”,“日”,“月”,“年”
Data_set:
Data 0 , 128, 129 , 130 , 131 , 132, 133  '秒”,“分钟”,“小时”,“日”,“月”,“年”
$include "../font8x8.font"
$include "../font12x16.font"  

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:595904 发表于 2020-12-30 01:56 | 显示全部楼层
受益了 !
回复

使用道具 举报

ID:595904 发表于 2020-12-30 02:01 | 显示全部楼层
正在为老版本的程序不能在新版本中运行伤脑筋,楼主的这篇文章为我解惑了。非常感谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表