In the spirit of the new forum area...Here's some code

Started by Transmogrifox, March 03, 2006, 03:18:05 AM

Previous topic - Next topic

Transmogrifox

So I have a little PIC12F683 eight pin device pulsing an LED with the PWM register in front of me on a breadboard.  It's a tap-tempo triangle wave LFO.  I've heard the 8-bit LFO's get to sounding a little grainey.  I haven't tried it yet on my filter or a trem circuit to see if this is true, but the concept works well and grainey or not, it's useable.  Some filtering on the output should remedy the grainey sound (it's just extra parts is the issue).  If I used a crystal and sped it up to 20 MHz on the clock, I may be able to get the full 10 bits from the PWM register.  I'll save for later experimenting.

With no further ado, here's the code to do what I've done.  Perhaps others who are into this PIC on guitar FX thing will have some ideas to contibute for making it better....Let me know if any of you assemble it and take it for a test drive.


; FILE: Tap.asm
; AUTH: R.Billing
; DATE: 03/02/2006
; DESC: Tap Tempo Low Frequency Oscillator.  Output on PWM pin
; as calculated triangle wave.  TMR1 (16-bit timer register) is
; preset with a value so to overflow every 12.8 (+/-) milliseconds.
; PER_CT increments on every overflow as a more coarse time counter.
; regA stores final count from PER_CT, and this count is used to
; set output sample rate where 1 iteration through the sample rate
; delay loop is at 1/512 of 12.8ms (about 50us).  Total period
; contains 512 samples.  The count in regA therefore ties the link
; between output sample rate and time elapsed between first button
; tap, and second button tap.
;
; Operation on the outside looks like this:
; Tap button once, TMR1 starts counting, GP4 turns on (used for
; indicator LED to see when it's "recording".  Tap button a
; second time, timer stops, turns off GP4, and writes new sample
; rate to oscillator.

; My disclaimer:  I didn't go through and do instruction cycle
; counting on each bit of code, so the measured time between taps
; may be gaining or losing time during execution of instructions,
; though each instruction takes up a whole 0.5 microseconds, so
; it's probably not noticeable.  The theory (algorithme) checks out
; on paper, and in Scilab.  It's just ASMOP :-)

;-------E---------------------------------------------------------------
;   cpu equates (memory map)

   INCLUDE P12F683.INC

PW_TX      EQU   0x20
PW_TEMP      EQU   0x21
PW_DEC      EQU   0x22
regA      EQU   0X23
decA      EQU   0X24
PER_CT      EQU   0X25

;----------------------------------------------------------------------

   GOTO   MAIN

;*********************************************************************
;
;  Interrupt Service Routine (ISR)
;
;*********************************************************************

   ORG      0x4
   
   BTFSC   PIR1,0      ;Check if tmr1 interrupt
   GOTO   timer_oflow
   CALL   debounce
   
   MOVLW   b'01000000'
   XORWF   INTCON,F         ;toggle timer interrupt
   GOTO   notlow

timer_oflow
   MOVLW   0xA3         ;100 ck cycles on TMR1l w/ compensation for 6 cycles during interrupt reset
   MOVWF   TMR1L
   CLRF   TMR1H   
   BSF      GPIO,4

   MOVLW   0x1
   ADDWF   PER_CT,F      ;Increment period counter so it sets carry bit
   BTFSS   STATUS,0      ;Test carry bit
   GOTO   clr_tmr_flg

   BCF      INTCON,6      ;Disable tmr1 interrupt if regA overflows
   MOVF   PER_CT, W
   MOVWF   regA         ;Save countable period
   CLRF   PER_CT
   BCF      GPIO,4

clr_tmr_flg
   BCF      PIR1,0         ;Clear interrupt flag
   RETFIE   


notlow
   CALL   debounce
   
   BTFSC   GPIO,5
   GOTO   notlow         ;ensure pin is low

   MOVF   PER_CT,W
   BTFSS   INTCON,6
   MOVWF   regA
   BTFSS   INTCON,6
   CLRF   PER_CT

   MOVLW   0x9C
   MOVWF   TMR1L
   CLRF   TMR1H
   BCF      GPIO,4         ;"Timing" LED off
   BCF      INTCON,0      ;clear interrupt on change flag
   nop

   RETFIE

;*********************************************************************
;
;   MAIN PROGRAM
;
;*********************************************************************

MAIN

   BCF      STATUS,5      ;Bank 0
   CLRF   GPIO
   CLRF   CMCON0
   BSF      STATUS,5
   CLRF   ANSEL
   MOVLW   b'00100000'
   MOVWF   TRISIO         ; All GPIO configured as outputs
   MOVLW   b'11110000'
   MOVWF   OSCCON
   BSF      IOC, 5         ;SET GP0 AS INTERRUPTIBLE PIN
   BCF      STATUS,5
   CLRF   INTCON

   CLRF   T1CON
   BSF      T1CON,0      ;timer1 on
   BSF      STATUS,5
   BSF      PIE1,0      ;timer1 ouverflow interrupt enable
   BCF      STATUS,5
   BCF      PIR1,0      ;clear interrupt flag for kicks and chix

   CLRF   T2CON
   BSF      T2CON,2      ;timer2 on
   BCF      INTCON,0   

   BSF      INTCON,GIE
   BSF      INTCON,GPIE      ;enable interrupt on GPIO change

   MOVLW   0xC      ;CCP CONFIGURATION BITS FOR PWM MODE
   MOVWF   CCP1CON
   BSF      STATUS,5   ;Bank 1 select
   MOVLW   0x3F
   MOVWF   PR2
   BCF      STATUS,5   ;Bank 0 select

   MOVLW   0xAA
   MOVWF   regA      ;Presest default LFO rate

loop_forever
   NOP

FORWARD
   CALL   sample_delay
   MOVLW   0x1
   ADDWF   CCPR1L,F   
   BTFSS   STATUS,0
   GOTO   FORWARD

   MOVLW   0xFF
   MOVWF   CCPR1L         ;set PWM reg to max

REVERSE

   CALL   sample_delay

   DECFSZ   CCPR1L         ;TRIANGLE LFO
   GOTO    REVERSE



   GOTO   loop_forever


;*************************************************************************
;    SUBROUTINE--debounce
;   Debounce switches
;*************************************************************************

debounce
   MOVLW   0x05
   MOVWF   PW_DEC
outer
   MOVLW   0xFF
   MOVWF   PW_TEMP
dloop
   DECFSZ   PW_TEMP
   GOTO   dloop

   DECFSZ   PW_DEC
   GOTO   outer

   CLRF   PW_DEC
   CLRF   PW_TEMP

   RETURN

;************************************************************************

;*************************************************************************
;    SUBROUTINE-sample_delay
;   Sets output sample rate to change LFO speed
;   Input:  regA   , holds number of iterations for 12.7 ms loop
;*************************************************************************

sample_delay

   MOVF      regA,W
   MOVWF      decA

sample_outer
   
   MOVLW      0x13
   MOVWF      PW_TEMP

sample_inner
      NOP
      NOP
      DECFSZ      PW_TEMP
      GOTO      sample_inner
   
   nop
   DECFSZ      decA
   GOTO      sample_outer   


   CLRF      PW_TEMP
   RETURN

   end



;----------------------------------------------------------------------
;   Developed in MPLAB
; at blast time, select:
;   memory uprotected
;   watchdog timer disabled
;   internal clock/no clkout  (8 MHz)
;   power-up timer on
;
;    If not using MPLAB IDE, add a line in the header to set confinguration bits
;   according to the above before assembling

trans·mog·ri·fy
tr.v. trans·mog·ri·fied, trans·mog·ri·fy·ing, trans·mog·ri·fies To change into a different shape or form, especially one that is fantastic or bizarre.

DavidS

Urk. I've been doing object-oriented programming in high-level languages, all those gotos and the lack of nice curly-braces scares the hell out of me. I've never coded in assembler!

Time to roll up the sleeves, I guess.

Thanks for sharing this, I'll be back to reread it sometime soon, I'm sure.

radio

At least I understood some pieces of the code,still have to become

less afraid of the hardware!Thanks for that stunning example!!!

Greetings JME
Keep on soldering!
And don t burn fingers!

Peter Snowberg

Eschew paradigm obfuscation

David

Hey, Peter and Trans:

That debounce thing is probably something that we're going to have to use a lot.  I recall that there's some debounce code in my bass pedal program (of course, I don't have the annotated source anymore since my  *&*(_)_)*((_*&*^& prior computer took a catastrophic head crash).  I recommend that we see if there's a way to turn a debounce procedure into some kind of generalized subroutine or macro so it just becomes one of the things we do.

By the way, your header looks like a good first step toward a standard format.

David

Quote from: DavidS on March 03, 2006, 04:39:07 AM
Urk. I've been doing object-oriented programming in high-level languages, all those gotos and the lack of nice curly-braces scares the hell out of me. I've never coded in assembler!

Time to roll up the sleeves, I guess.

Thanks for sharing this, I'll be back to reread it sometime soon, I'm sure.

You can still write elegant assembler code.  It just takes a little more work.

R.G.

QuoteWith no further ado, here's the code to do what I've done.  Perhaps others who are into this PIC on guitar FX thing will have some ideas to contibute for making it better
Great stuff. It looks a lot like some of my LFO stuff. Of course, LFOs are not all that different one from another.

Quote
QuoteUrk. I've been doing object-oriented programming in high-level languages, all those gotos and the lack of nice curly-braces scares the hell out of me. I've never coded in assembler!
You can still write elegant assembler code.  It just takes a little more work.
Ah! There's someone who's written some code with his eyes open. Darn right! The discipline in structuring your code can just as well come from your head as curly braces. In fact, if you have the discipline to write well-structured code, you don't need crutches like languages that force you to declare and stick in matching braces. Just like a true wood craftsman can turn out out masterpieces with only hand tools. He may go faster with power tools, but the artistry is there whether or not he has the fancy stuff.

For good programming THE LANGUAGE DOES NOT MATTER. It's how you organize your thoughts.

Just as a tip for those of you who never used assembler - it's entirely possible with a good macro assembler to define macros which amount to all of the structured statements and program only in the structured stuff. Hey - that sounds like how a compiler does it... hmmm...

QuoteThat debounce thing is probably something that we're going to have to use a lot. ... I recommend that we see if there's a way to turn a debounce procedure into some kind of generalized subroutine or macro so it just becomes one of the things we do.
Another really, really important concept - don't reinvent any more than you have to.

One thing you could do is to got to the PICList and look up "debounce". You'll turn up lots of examples of debouncers written by experts, previously tested, and free for the asking. Back when I first did my eight way footswitcher I found a so-called "vertical adder" there that debounces all eight footswitches at the same time, with the same code, and leaves the state of all eight switches in one byte. It let me assign individual switches to alternate action or momentary with a "switch action" byte in the code.

In the words of the immortal Tom Lehrer:
QuoteI think of great Lobachevski and I get idea... plagiarize! Let no one else's work evade your eyes, so plagiarize, plagiarize, plagiarize.
Only please always to call it research.

QuoteBy the way, your header looks like a good first step toward a standard format.
Absolutely. A header is mandatory, not optional.
R.G.

In response to the questions in the forum - PCB Layout for Musical Effects is available from The Book Patch. Search "PCB Layout" and it ought to appear.

Peter Snowberg

I've never understood the obsession with getting rid of GOTO instructions in "higher level" languages. It always seemed to me that if it was good enough for the CPU to speak, why shouldn't I?

Debouncing is something that we will do quite a bit of around here, but in the microcontroller assembly world it's often something that you do while you do other things. You can't always make something for I/O into a nice little packaged library like you do in C, but you can make snippets that perform the same function which are then placed in-line with other code. The way the switch may be hooked up in the circuit can vary all over the place, so very often the snippet needs a little customization for your hardware. There are several ways to accomplish debouncing but you can think of them as very low resolution digital low-pass filters. ;)

As R.G. just mentioned, there are sources of zillions of standard I/O routines on the net.  :icon_biggrin:
Eschew paradigm obfuscation

R.G.

QuoteI've never understood the obsession with getting rid of GOTO instructions in "higher level" languages. It always seemed to me that if it was good enough for the CPU to speak, why shouldn't I?
Yeah - real manly-guy programmers program right down to the bare metal!

It took me a while to figure that one out. I was extruded through some formal proof-of-correctness programming stuff. It turns out that GOTOs are perfectly fine for that. The problem is in the programmer's head. It turns out that a GOTO is always done for a side effect. It's practically never just to neatly organize ideas. It's about 99% used for implementing something like testing a condition, then conditionally executing some code. Problem is, once you have written the code, a week later you can't remember the side effects you were depending on to make that GOTO work properly.

The whole nonsense of WHILE...END, IF... THEN...ELSE...END, CASE X ... CASE END, GOSUB,  and later on the whole idea of Java classes is to sledgehammer-force programmers to have a clearly written out reason for why they took that jump. The idea is that if you can't make the programmers better, at least you can put training wheels on the language to force them to do it right.

OK, I'm ranting now. It's more like using safety guards on machine tools. If there is a safety guard there, you usually can't hurt yourself by being inattentive. You can take the guards off and still get hurt, but presumably it's because you wanted to get rid of those pesky excess fingers.

Another way of looking at it is that as a society we needed a big slug of programmers, and we used up all the talented great ones too fast. We scraped the barrel harder by raising programmer salaries so high that people who didn't really have huge aptitudes for the work decided to program. That worked, but it was a lot more productive if we didn't let the lesser-skilled ones hurt themselves and others by coloring outside the lines. It's a productivity measure.

The funny thing about prohibiting GOTOs in uCs is that in the limited memory space and relative cluttering of the space with special purpose registers, GOTOs really do have a place to restructure code logically.

... and GOTOs are GREAT!!! for confusing someone else about what your code is doing...    :icon_biggrin:
R.G.

In response to the questions in the forum - PCB Layout for Musical Effects is available from The Book Patch. Search "PCB Layout" and it ought to appear.

David

Quote from: Peter Snowberg on March 03, 2006, 09:51:42 AM
I've never understood the obsession with getting rid of GOTO instructions in "higher level" languages. It always seemed to me that if it was good enough for the CPU to speak, why shouldn't I?

I'll give you two reasons, but I'll venture to guess that few people here will understand them:

1)  The GOTO DEPENDING ON statement
2)  The ALTER statement


David

Quote from: Peter Snowberg on March 03, 2006, 09:51:42 AM
You can't always make something for I/O into a nice little packaged library like you do in C, but you can make snippets that perform the same function which are then placed in-line with other code.

Touche.  OK, then, some judiciously-written INCLUDE files for incorporation at the proper place in the code.  You like?

Transmogrifox

Quote from: David on March 03, 2006, 11:45:16 AM
Quote from: Peter Snowberg on March 03, 2006, 09:51:42 AM
You can't always make something for I/O into a nice little packaged library like you do in C, but you can make snippets that perform the same function which are then placed in-line with other code.

Touche.  OK, then, some judiciously-written INCLUDE files for incorporation at the proper place in the code.  You like?

It sounds reasonable to me.  IDE's generally make such things very easy to do with object linkers and such.

Does Atmel have a free IDE for working with their devices?
trans·mog·ri·fy
tr.v. trans·mog·ri·fied, trans·mog·ri·fy·ing, trans·mog·ri·fies To change into a different shape or form, especially one that is fantastic or bizarre.

DavidS

Atmel does have a free IDE. I played with it a little last night. Built-in simulator/debugger. I didn't do too much with it, just ran some example code and fiddled with it a little, but the program seems pretty nice.

jrem

Quote from: R.G. on March 03, 2006, 10:27:11 AM
The whole nonsense of WHILE...END, IF... THEN...ELSE...END, CASE X ... CASE END, GOSUB,  and later on the whole idea of Java classes is to sledgehammer-force programmers to have a clearly written out reason for why they took that jump. The idea is that if you can't make the programmers better, at least you can put training wheels on the language to force them to do it right.

not to argue with the expert(s) (but I do with my wife, so why not here?) but next someone is going to say there is no use for jsr's or call's or btfsc's . . .


H.Manback

Been a while since I've seen assembly code, I tend to stay away from that stuff :icon_mrgreen:.

About the GOTO statement, I don't think I will ever understand why there are programmers who stick to the idea 'If it was good enough for my grandpa in 1955, it's good enough for me!'. The reason why goto is evil was published in 1968 by Dijkstra (http://www.acm.org/classics/oct95/). In higher level languages, the necessity of goto is near non-existent. The control flow just does everything for you, and makes it a lot easier to read too.

I don't really agree with the view that all this fancy while end do if stuff was just to force monkeys to be able to program in a understandable way. If it was actually meant to do that, it failed horribly... I think it has more to do that you can write code a lot more compact and therefore easier to read. Try making a for loop in assembly and you'll see what I mean. Programs got bigger and bigger and more complicated so low level languages just became too much of a hassle, not to mention it being more error prone.

Peter Snowberg

For the record, my Grandpa worked for the Post Office.  :icon_wink:

To restate my argument, the CPU uses GOTO (or JMP) as one of it's very few instructions.

Why should I be forced to exclude this perfectly valid instruction in order to follow somebodies academic and linear concept of saving me from myself when it's just not necessary?

A lot of code I've written could not be written without jumps because of cycle or stack space restrictions. JMP isn't just an option in microcontroller land, sometimes it's imperative. The early PICs only had two words of stack; not exactly CALL friendly.

I'm an assembly programmer, and always will be. :icon_biggrin:


Anyway, let us turn back to Transmogrifox's code. 8) 8) 8)

Who is going to be the first one to make it watch four foot taps and average the tap rate?
Eschew paradigm obfuscation

jrem

Quote from: Peter Snowberg on March 04, 2006, 08:32:47 PM
Who is going to be the first one to make it watch four foot taps and average the tap rate?

well, on a quick glance it looks like he's already looking for the switch on the ISR, so it shouldn't be too hard to set two eight bit words for the number of cycles between switch contacts, then add them and divide for the average . . .

Transmogrifox

Quote from: jrem on March 05, 2006, 07:41:17 PM
Quote from: Peter Snowberg on March 04, 2006, 08:32:47 PM
Who is going to be the first one to make it watch four foot taps and average the tap rate?

well, on a quick glance it looks like he's already looking for the switch on the ISR, so it shouldn't be too hard to set two eight bit words for the number of cycles between switch contacts, then add them and divide for the average . . .

I think the addition of some conditionals in it would be necessary.  Give it a timeout condition so if you only give it two or three taps, it doesn't screw up your tempo the next time when you try to go to a much slower of faster rate by averaging the old tap times with your intended tempo.

Still not hard to do.  If anyone knows of links to some simple 8-bit averaging algorithms, or even division, that would be a good start.

Division in an 8-bit uC is not real straight forward operation.  For the addition, you would have to alot each sample two registers to deal with the carry bit.  If you divide first, then add, you may lose a little resololution...but if you're off by 3 ms, who is going to detect that?  It would save the trouble of dealing with carry bits and the extra registers.
trans·mog·ri·fy
tr.v. trans·mog·ri·fied, trans·mog·ri·fy·ing, trans·mog·ri·fies To change into a different shape or form, especially one that is fantastic or bizarre.

DavidS

Yeah, but off by 3ms isn't off by 3ms each beat, it's additive. So you're off 3ms, 6ms, 9ms, 12ms, etc. At 250ms tempo, you're off 720ms after a minute. OK, yeah, not a huge deal...

Dave_B

If you looked at the last tap relative to the first, wouldn't you automatically have the average if you divided by four? 
Help build our Wiki!