Author Topic: PIC16F54 Buzzer will not turn off  (Read 1345 times)

0 Members and 1 Guest are viewing this topic.

Offline AztlanpzTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: us
PIC16F54 Buzzer will not turn off
« on: January 18, 2020, 01:41:30 am »



Code: [Select]
;************************************************************
; written by: XXXXXXXXX     *
; date: 1/14/2020     *
; version: 1.0     *
; file saved as: LED_Buzzer.asm     *
; for PIC16F54     *
; clock frequency:  2.4576 MHz     *
;***********************************************************
; PROGRAM FUNCTION: Turns on an LED on PoartA, and a Buzzer
;     on PortB
;___________________________________________________________
    #Include <p16f54.inc>
    __CONFIG _OSC_XT & _WDT_OFF & _CP_OFF
;===========================================================
; Declarations:
   
porta     equ     05
portb     equ     06

Mark30     equ     07
Post80     equ     08
_5Second    equ     09

org 1FFh
goto Start
org 0
;==========================================================
; Subroutines:
Init movlw b'00000111' ;set up TMR0 to count
option ;internally, prescaled by 256

clrf PORTA ; resets input/output ports
clrf PORTB
movlw b'00000' ; sets up which pins are inputs
tris PORTA ; and which are outputs
movlw b'00000000'
tris PORTB
retlw 0
;=========================================================
; Program Start:
Start
call Init

movlw d'30' ;moves the decimal number 30 into
movwf Mark30 ;the GPF called Mark30, the marker

movlw d'80' ;moves the decimal number 80 into
movwf Post80 ;the GPF called Post80, the first
;postcaler

movlw d'5' ;move the decimal number 5 into
movwf _5Second ;the GPF called _5Second


TimeLoop
movfw Mark30 ;takes the number out of Mark30
subwf TMR0,w ;subtracts this number from the
;number in TMR0, leaving the result
;in the working register (and leaving
;TMR0 unchanged)
btfss STATUS,Z ;test the zero flag-skip if set,i.e. if
;the result is zero it will skip the next
;instruction
goto TimeLoop ;if the result isn't zero, it loops back to
;'TimeLoop'

movlw d'30' ;moves the decimal number 30 into
addwf Mark30,f ;the working register and then
;adds it to Mark30

decfsz Post80,f ;decrements Post80, and skips the next
;instruction if the result is zero
goto TimeLoop ;if the result isn't zero, it loops back to
;'TimeLoop'

;When it reaches this point, 1 second
;has passed
movlw d'80' ;resets Post80, moving the number 80
movwf Post80 ;back into it

call LEDToggle
bcf PORTB,RB0 ;turns buzzer of

decfsz _5Second,f ;check if 5 seconds have pass and turns on
;Buzzer
call TurnBuzzer

goto TimeLoop

TurnBuzzer

bsf PORTB,RB0 ;turns buzzer on
movlw d'5' ;move the decimal number 5 into
movwf _5Second ;the GPF called _5Second
retlw 0x00

LEDToggle

movf PORTA,w ;Move PORTA to the working register
xorlw b'0001' ;Toggle bit RA0 from PORTA
movwf PORTA ;
retlw 0x00

    END

I wrote a program in assembly to blink and LED on PortA bit0 every second and to turn on a buzzer. On PortB, bit0 beeps every 5 seconds nut once the buzzer activates it will produce a continuous tone.
 

Online cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: PIC16F54 Buzzer will not turn off
« Reply #1 on: January 18, 2020, 04:20:29 pm »
Without going through the trouble of trying to read asm, even though should be simple, I would guess you are not taking into account timer0 will be at a certain number for 256 clocks so are probably getting messed up at that point.

I'll suggest something that you may not want to do, but why not just take advantage of the xc8 compiler. You get something everyone can read so help is easier to come by, you can get it right quicker, and it produces roughly the same size code. In this case you can even use a delay instead of the timer if wanted.

This compiles to around ~68 'words' (13% of flash), which is probably not too far from what you have now-

#include <xc.h>
#include <stdint.h>
#define _XTAL_FREQ 2457600

void led_on(){ RB0 = 1; }
void led_off(){ RB0 = 0; }
void led_tog(){ RB0 = !RB0; }
void buzzer_on(){ RA0 = 1; }
void buzzer_off(){ RA0 = 0; }
void init(){
    TRISA=0;
    TRISB=0;
    led_off();
    buzzer_off();
    OPTION=7; //timer0 prescale 256
}
//void delay_ms(uint16_t ms){ for( ; ms--; __delay_ms(1) ); }
void wait_1s(){
    //_XTAL_FREQ/prescale = overflows needed for 1s = 9600
    for(uint16_t i = 0; ; ){
        while( TMR0 != 0 ); //wait for 0
        if( ++i >= 9600 ) return; //done?
        while( TMR0 == 0 ); //wait for NOT 0
    }
}
void main(void) {
    init();
    for(uint8_t i = 0; ; ){
        //delay_ms(1000);
        wait_1s();
        led_tog();
        buzzer_off();
        if( ++i >= 5 ){
            buzzer_on();
            i = 0;
        }
    }
}
 

Offline KL27x

  • Super Contributor
  • ***
  • Posts: 4108
  • Country: us
Re: PIC16F54 Buzzer will not turn off
« Reply #2 on: January 20, 2020, 06:09:10 pm »
The nice feature of assembly is that you can insert break points or flags in your code to figure out what is happening.

You have done a lot of unconventional (read weird) things with your code. I think you have already gotten too far along without verifying. Your one second timing code is not readily apparent. It 80 cycles of 30 clicks of the timer equal to one second?

The conventional way do do this would be to check the timer when it reaches your mark (of say 30); when it does, you increment another counter/postscaler if necessry AND you set the timer register back to 0. The way you letting the timer roll and then incrementing the mark 30 register by 30 is just weird. This wraps beyond 256, of course, which should be fine if the timer is 8 bit; but this is weird. And you can't do a carry check with this method, because of the eventual wrap around.

 Also, it's possibly fine in this exact case that you are doing subtractions and zero checks on the timer. But in general, you ought to do carry checks, which will be more full proof, in case the code loop does not execute fast enough to catch the timer on that particular tick. As your code gets more complex, with more potential branch possibilities, this will be harder to determine.




Your chief complaint is that the buzzer does not turn off. So I assume you are getting your one second intervals good enough.

The initialization subroutine is only called once. The only other place that turns off the buzzer is part two of your Timeloop. You might want to place some test code there to see if this ever gets executed beyond the first time.

Another part that appears strange is :
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   decfsz   _5Second,f   ;check if 5 seconds have pass and turns on
            ;Buzzer
   call   TurnBuzzer
        goto Timeloop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I dunno. Maybe this is as intended, but it appears your logic is backwards. But maybe I don't understand what you're doing, here with the 5 second timer.
 

Offline KL27x

  • Super Contributor
  • ***
  • Posts: 4108
  • Country: us
Re: PIC16F54 Buzzer will not turn off
« Reply #3 on: January 21, 2020, 09:40:35 am »
Thanks to insomnia, I looked again at your code.

I am assuming that:
1. Using your external clock, that 30 ticks of tmr0 is about an 80th of a second; I assume that part is working the way you intended.
2. Your buzzer turns on after 1 second of run time and does not turn off.
3. Your LED first turs on at this exact same time, at 1 second. And that it continues to toggle on/off every 1 second, thereafter. But the buzzer does not turn off.
4. I further assume you intended the buzzer to turn on at 5 seconds and to remain on for 1 second.

If all of this is correct, then what I copied/highlighted in my first post is surely your problem. You just had dyslexia on your skip if instruction. The first time the 1 second time gate was reached, the LED toggled, the buzzer pin was cleared, but the Buzzer subroutine got called 2 instructions later (which was not your intention). The Buzzer subroutine resets the 5-second register, so this result is always the case every subsequent time the 1 second gate is passed.

Code: [Select]
   call   LEDToggle
   bcf   PORTB,RB0   ;turns buzzer of

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   
;   decfsz   _5Second,f   ;check if 5 seconds have pass and turns on
            ;Buzzer
;_____________________________________________________
        decf  _5second,f
        btfsc  STATUS,Z ;         skip the next instruction if the result is NOT zero
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   call   TurnBuzzer
        goto  TimeLoop

You could also do this:

decfsz _5second,f
goto     TimeLoop
call       TurnBuzzer
goto     TimeLoop



The buzzer should turn on at 5, turn off at 6, turn on at 10 turn off at 11.


RB0 should have a square wave 20% duty cycle with 5 second period, on for 1, off for 4. This is in exclusion of the first-run after reset. It should remain digital output low for the first 5 seconds.

RA0 should have a square wave of 50% duty cycle with 2 second period. It should start as digital output low for the first second, then first switch to output hi at exactly 1 second.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf