Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm AVR组件中无PWM产生方波_Algorithm_Assembly_Avr - Fatal编程技术网

Algorithm AVR组件中无PWM产生方波

Algorithm AVR组件中无PWM产生方波,algorithm,assembly,avr,Algorithm,Assembly,Avr,我用的是ATmega328。问题是我想产生一个给定频率和振幅的方波。PWM不能使用,因为我得到了一块已经焊接好的电路板,所以波形必须放在连接到处理器B端口的a的输出端。因此,基本上,我的想法是,我必须周期性地将端口B的引脚放入0和VOLUME(VOLUME是一个从1到255的数字),具有给定的频率和50%的占空比。请记住:无PWM。频率应该可以每100毫秒改变一次,但我不能让它工作,所以我只是想产生一个恒定的频率,看看一开始会发生什么 我正在以1MHz的频率运行时钟。我编写了以下代码: .DSE

我用的是ATmega328。问题是我想产生一个给定频率和振幅的方波。PWM不能使用,因为我得到了一块已经焊接好的电路板,所以波形必须放在连接到处理器B端口的a的输出端。因此,基本上,我的想法是,我必须周期性地将端口B的引脚放入0和VOLUME(VOLUME是一个从1到255的数字),具有给定的频率和50%的占空比。请记住:无PWM。频率应该可以每100毫秒改变一次,但我不能让它工作,所以我只是想产生一个恒定的频率,看看一开始会发生什么

我正在以1MHz的频率运行时钟。我编写了以下代码:

.DSEG
.ORG 0x100

.CSEG
.ORG 0x100

;Initializing stack pointer
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16

MAIN:
CALL GENERATE ;Calling the generating routine
RJMP MAIN ;Repeat this forever

;I will generate a 440Hz frequency. It has an approximate period of 2273 microseconds
;This means that half a period stands for approximately 1136 clocks
GENERATE:
LDI R17, 0x70
LDI R18, 0x04   ;Half the period in hexadecimal is 0x0470
LDI R19, 243    ;Volume = 243 (arbitrary, it could be any number)
LDI R21, 88 ;The amount of half-periods in 100 ms (arbitrary election, too)

LDI R25, 0xFF
OUT DDRB, R25       ;Port B is an output port

LDI R24, 0xFF       ;R25R24 = 0xFFFF
CLC         ;Clean the carry
SBC R24, R17
SBC R25, R18        ;R25R24 = 0xFFFF - Halfperiod
ADIW R25:R24, 1     ;R25R24 = 0xFFFF - Halfperiod +1
OUT PORTB, R18      ;The wave starts at 0

BEGIN:
CALL LOOP_1
EOR R19, R19        ;It varies between 0 and volume
OUT PORTB, R19      ;It puts the output to the actual value of R19 (0 or volume)
CLZ         ;Clean Z flag
DEC R21
BREQ END        ;When 100ms have passed, generation is over
JMP BEGIN       ;If not, generation continues

LOOP_1: STS TCNT1H, R25
STS TCNT1L, R24     ;Loading the amount of clocks the timer has to count
LDI R16, 0x00
STS TCCR1A, R16
LDI R16, 0x01
STS TCCR1B, R16     ;Timer operating in normal mode, no prescaler
LOOP_2: IN R16, TIFR1
SBRS R16, TOV1      ;If timer's over, skip the next jump
JMP LOOP_2
LDI R16, 0x00
STS TCCR1B, R16     ;Stopping the timer
LDI R16, 0x04
OUT TIFR1, R16      ;Clean TOV1
RET         ;Back to BEGIN

END:
RET         ;Back to MAIN
这是我的第一个组装方法之一,所以读起来可能很难看。代码当前不起作用。有什么想法吗

编辑:

感谢Spektre向我指出这一点,我更正了上面的一段代码。代码相同,只是

GENERATE:
.
.
.
LDI R21, 44 ;The amount of PERIODS (not half-periods as before) in 100 ms
.
.
.
BEGIN:
OUT PORTB, R18      ;This was before the BEGIN tag, now it is after it
CALL LOOP_1     ;It counts a halfperiod with output=0
OUT PORTB, R19      ;Now output=volume
CALL LOOP_1     ;It counts a halfperiod with output=volume
CLZ         ;Clean Z Flag
DEC R21
BREQ END        ;When 100ms have passed, generation is over
JMP BEGIN       ;If not, generation continues

多年来我为这样的建筑做了些什么。。。我正在使用UC3(后继者),在MCU上,我可以获得2-5MHz的轮询频率。但我担心在你的目标平台上,这会低很多。因此,首先测量轮询频率,以查看HW的限制

1 MHz
是输出时钟还是MCU时钟?输出时需要的最大频率是多少

实现这一目标的方法还有很多,显而易见的是:

  • 计时器/计数器

    您可以为示例1 us设置计时器,并在那里进行计时和切换。或者你可以用它来设置新的频率。但请注意,任何中断都可能导致输出信号出现抖动等定时问题。。。如果有足够的时间,这些可以避免,但它通常会限制最大输出频率很多

  • 主线程轮询

    看起来这就是你正在做的。但是你忘记了所有的指示都有自己的时间。条件句可以有不同的时间,所以你应该补偿

  • 利用接口(如有)

    查看数据表,查看是否有任何HW连接到该端口MCUs通常在引脚上多路复用更多功能,有时允许利用这些功能。例如,我曾经利用串行接口、DMA+ABI/SD卡接口和其他接口制作了一个VGA图像生成器。每种方法都能正确地产生16色VGA信号,但在同一芯片和引脚上具有完全不同的代码和硬件结构。。。与您在ATMega168上使用
    20MHz
    时钟(与ATMega328的核心相同)的平台相比,我能够为LCD产生1bitLCD的640x480信号(比彩色VGA信号所需的信号低很多倍)。有时您甚至可以在运行时更改管脚的含义。。。例如,为了实现
    100000 rpm
    BLDC控制器,我改变了几次内部电路和电机的电气周期,以便能够产生如此高的速度(否则无法通过直接方式实现)

  • 您需要试验哪种方法对您来说足够快/准确/舒适。从第一眼看到您的代码,我就发现:

    EOR R19, R19        ;It varies between 0 and volume
    OUT PORTB, R19      ;It puts the output to the actual value of R19 (0 or volume)
    
    如果我看对了,您是XORing
    R19
    R19
    …在第一次通过后,对
    R19
    的内容进行解压。因此,您一直在将
    0
    输出到
    B
    端口:

    XOR Volume,Volume -> Volume = 0
    XOR 0,0  -> 0
    
    相反,我会:

  • 输出端口容量
  • 等待半段时间
  • 输出端口0
  • 等待半段时间
  • 如果100毫秒达到更改频率
  • goto#1
  • 您应该尽可能少地使用端口访问。不确定ATMega是否也是如此,但在较新的体系结构上,端口是通过HW接口API访问的,如果不明智地使用,则高速切换的速度非常慢

    PS.

    希望您的R2R梯形图在输入电阻器之前有二极管,或者至少PORTB有集电极开路输出,否则由于相邻管脚之间的电流,如果两者设置为不同的值,它将无法工作

    [Edit1]一些澄清

  • 更清楚R2R当前的问题

    二极管应具有相同的PN势垒电压

  • 1MHz时钟

    您编写了
    我正在以1MHz运行时钟,但没有说明它是MCU/CPU时钟、计时器时钟还是输出方波信号频率。现在很明显,您的输出信号是
    Hz
    ,这在您的平台上应该很容易实现。我以前不确定…我在m上甚至产生了30 MHz左右的信号CU的1兆赫频率是可能的

  • 定时

    大多数平台上的条件指令(如
    BREQ END
    )在条件为真和为假时具有不同的定时,因此为了保持同步,您应该考虑到这一点……有时放置良好的
    nop
    可以处理问题……如果是情况或错误,您应该检查文档中的指令定时ot

    你说你正在使用计数器/计时器,我在你的代码中没有看到。为了正确使用,你应该

  • 为您想要的模式和频率配置硬件
  • 注册中断子程序
  • 启用中断和/或触发计时器计数器事件
  • ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; generator ver: 0.00 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    /*
    CPU:    ATMega168 8MHz internal RC
    
    OUT:    PC
    */
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; ATMega168 startup: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; fuses:BOOTRST=1 disabled
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            .equ stack  =0x04FF
            .equ cpuclk =8000000
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            .device ATmega168   ; device selection
            .equ PINB  =0x03    ; IO reg adresses definition in/out
            .equ DDRB  =0x04
            .equ PORTB =0x05
            .equ PINC  =0x06
            .equ DDRC  =0x07
            .equ PORTC =0x08
            .equ PIND  =0x09
            .equ DDRD  =0x0A
            .equ PORTD =0x0B
            .equ TIFR0 =0x15
            .equ TIFR1 =0x16
            .equ TIFR2 =0x17
            .equ PCIFR =0x1B
            .equ EIFR  =0x1C
            .equ EIMSK =0x1D
            .equ GPIOR0=0x1E
            .equ EECR  =0x1F
            .equ EEDR  =0x20
            .equ EEARL =0x21
            .equ EEARH =0x22
            .equ GTCCR =0x23
            .equ TCCROA=0x24
            .equ TCCROB=0x25
            .equ TCNT0 =0x26
            .equ OCROA =0x27
            .equ OCROB =0x28
            .equ GPIOR1=0x2A
            .equ GPIOR2=0x2B
            .equ SPCR  =0x2C
            .equ SPSR  =0x2D
            .equ SPDR  =0x2E
            .equ ACSR  =0x30
            .equ SMCR  =0x33
            .equ MCUSR =0x34
            .equ MCUCR =0x35    
            .equ SPMCSR=0x37
            .equ SPL   =0x3D
            .equ SPH   =0x3E
            .equ SREG  =0x3F
    
            .equ WDTCSR=0x60    ; sts,lds
            .equ CLKPR =0x61
            .equ PRR   =0x64
            .equ OSCCAL=0x66
            .equ PCICR =0x68
            .equ EICRA =0x69
            .equ PCMSK0=0x6B
            .equ PCMSK1=0x6C
            .equ PCMSK2=0x6D
            .equ TIMSK0=0x6E
            .equ TIMSK1=0x6F
            .equ TIMSK2=0x70
            .equ ADCL  =0x78
            .equ ADCH  =0x79
            .equ ADCSRA=0x7A
            .equ ADCSRB=0x7B
            .equ ADMUX =0x7C
            .equ DIDR0 =0x7E
            .equ DIDR1 =0x7F
            .equ TCCR1A=0x80
            .equ TCCR1B=0x81
            .equ TCCR1C=0x82
            .equ TCNT1L=0x84
            .equ TCNT1H=0x85
            .equ ICR1L =0x86
            .equ ICR1H =0x87
            .equ OCR1AL=0x88
            .equ OCR1AH=0x89
            .equ OCR1BL=0x8A
            .equ OCR1BH=0x8B
            .equ TCCR2A=0xB0
            .equ TCCR2B=0xB1
            .equ TCNT2 =0xB2
            .equ OCR2A =0xB3
            .equ ASSR  =0xB6
            .equ TWBR  =0xB8
            .equ TWSR  =0xB9
            .equ TWAR  =0xBA
            .equ TWDR  =0xBB
            .equ TWCR  =0xBC
            .equ TWAMR =0xBD
            .equ UCSR0A=0xC0
            .equ UCSR0B=0xC1
            .equ UCSR0C=0xC2
            .equ UBRR0L=0xC4
            .equ UBRR0H=0xC5
            .equ UDR0  =0xC6
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
            .cseg           ; Start of code segment
            .org 0x0000     ; interrupts, highest priority first
            jmp RESET       ; 3 Reset Handler
            jmp EXT_INT0    ; 3 IRQ0 Handler
            jmp EXT_INT1    ; 3 IRQ1 Handler
            jmp PCINT0      ; 3 PCINT0 Handler
            jmp PCINT1      ; 3 PCINT1 Handler
            jmp PCINT2      ; 3 PCINT2 Handler
            jmp WDT         ; 3 Watchdog Timer Handler
            jmp TIM2_COMPA  ; 3 Timer2 Compare A Handler
            jmp TIM2_COMPB  ; 3 Timer2 Compare B Handler
            jmp TIM2_OVF    ; 3 Timer2 Overflow Handler
            jmp TIM1_CAPT   ; 3 Timer1 Capture Handler
            jmp TIM1_COMPA  ; 3 Timer1 Compare A Handler
            jmp TIM1_COMPB  ; 3 Timer1 Compare B Handler
            jmp TIM1_OVF    ; 3 Timer1 Overflow Handler
            jmp TIM0_COMPA  ; 3 Timer0 Compare A Handler
            jmp TIM0_COMPB  ; 3 Timer0 Compare B Handler
            jmp TIM0_OVF    ; 3 Timer0 Overflow Handler
            jmp SPI_STC     ; 3 SPI Transfer Complete Handler
            jmp USART_RXC   ; 3 USART, RX Complete Handler
            jmp USART_UDRE  ; 3 USART, UDR Empty Handler
            jmp USART_TXC   ; 3 USART, TX Complete Handler
            jmp ADC_DONE    ; 3 ADC Conversion Complete Handler
            jmp EE_RDY      ; 3 EEPROM Ready Handler
            jmp ANA_COMP    ; 3 Analog Comparator Handler
            jmp TWI         ; 3 2-wire Serial Interface Handler
            jmp SPM_RDY     ; 3 Store Program Memory Ready Handler
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    RESET:  cli             ; Reset Handler
            ldi  r16,high(stack)
            out  SPH,r16
            ldi  r16,low(stack)
            out  SPL,r16
    
            ldi  r16, 0xFF  ; DDRB 1 - output, 0 - input direction
            out  DDRB, r16
            ldi  r16, 0xFF  ; DDRC 1 - output, 0 - input direction
            out  DDRC, r16
            ldi  r16, 0xFF  ; DDRD 1 - output, 0 - input direction
            out  DDRD, r16
    
            ldi  r16, 0xFF  ; all outputs high,and all inputs Pull Up to Ucc
            out  PORTB,r16
            out  PORTC,r16
            out  PORTD,r16
            jmp  main
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    EXT_INT0:               ; IRQ0 Handler
            reti
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    EXT_INT1:               ; IRQ1 Handler
            reti
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    PCINT0:                 ; PCINT0 Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    PCINT1:                 ; PCINT1 Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    PCINT2:                 ; PCINT2 Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    WDT:                    ; Watchdog Timer Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM2_COMPA:             ; Timer2 Compare A Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM2_COMPB:             ; Timer2 Compare B Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM2_OVF:               ; Timer2 Overflow Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM1_CAPT:              ; Timer1 Capture Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM1_COMPA:             ; Timer1 Compare A Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM1_COMPB:             ; Timer1 Compare B Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM1_OVF:               ; Timer1 Overflow Handler 
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM0_COMPA:             ; Timer0 Compare A Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM0_COMPB:             ; Timer0 Compare B Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TIM0_OVF:               ; Timer0 Overflow Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    SPI_STC:                ; SPI Transfer Complete Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    USART_RXC:              ; USART, RX Complete Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    USART_UDRE:             ; USART, UDR Empty Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    USART_TXC:              ; USART, TX Complete Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ADC_DONE:               ; ADC Conversion Complete Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    EE_RDY:                 ; EEPROM Ready Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ANA_COMP:               ; Analog Comparator Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    TWI:                    ; 2-wire Serial Interface Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    SPM_RDY:                ; Store Program Memory Ready Handler
            reti            ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    main:   ldi  r16,0
    
    main0:  out  PORTC,r16  ; 1 8T / 8MHz = 1MHz PC0, 0.5MHz PC1, ...
            inc  r16        ; 1
            nop             ; 1
            nop             ; 1
            nop             ; 1
            nop             ; 1
            rjmp main0      ; 2
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    waitms: push r31        ; 2 wait cca r31 [ms] +(15+r31*4)T
            push r30        ; 2
            push r29        ; 2
    waitms2:ldi  r30,cpuclk/200000;1 1ms
    waitms1:ldi  r29,49     ; 1     200T
    waitms0:nop             ; 1
            dec  r29        ; 1
            brne waitms0    ; 2/1
            dec  r30        ; 1
            brne waitms1    ; 2/1
            dec  r31        ; 1
            brne waitms2    ; 2/1
            pop  r29        ; 2
            pop  r30        ; 2
            pop  r31        ; 2
            ret             ; 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; ID: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;