10-bit ADC to 10-bit PWM

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

10-bit ADC to 10-bit PWM

874 Views
Katsuke
Contributor I

Hello people!

 

I'm new with HCS08 and I'm having a problem in HOW to Send the ADCR to the PWM output.

 

We're using the MC9S08QG8 with Assembly language.

 

 

Here's the code:

 

 

;*******************************************************************
;* This stationery serves as the framework for a user application. *
;* For a more comprehensive program that demonstrates the more     *
;* advanced functionality of this processor, please see the        *
;* demonstration applications, located in the examples             *
;* subdirectory of the "Freescale CodeWarrior for HC08" program    *
;* directory.                                                      *
;*******************************************************************

; Include derivative-specific definitions
            INCLUDE 'derivative.inc'
;
; export symbols
;
            XDEF _Startup
            ABSENTRY _Startup

;
; variable/data section
;
            ORG    RAMStart         ; Insert your data definition here
ExampleVar: DS.B   1

;
; code section
;
            ORG    ROMStart

_Startup:
      LDA #%01100001
      STA ADCSC1                                                                       
      LDA APCTL1
      BSET 2, APCTL1
      LDA #%0100000
      STA ADCSC2
      LDA #%00110100
      STA ADCCFG
      LDA #%00001000
      STA TPMSC
      LDA #%00100000
      STA PTBDD
      BSET 5, PTBD
      LDA #%00000010
      STA PTADD
      BSET 1, PTAD

      LDA #$02
      STA SOPT1
      LDHX   #RAMEnd+1        ; initialize the stack pointer
      TXS
      CLI                     ; enable interrupts



        

mainLoop:
 
    
    
    
      BSR RandW

      NOP

     ; feed_watchdog
        BRA    mainLoop

  RandW:  ;function to read and write at the Channel 1 of the tpm
      LDHX ADCRH
      STHX TPMC1V
      RTS
;**************************************************************
;* spurious - Spurious Interrupt Service Routine.             *
;*             (unwanted interrupt)                           *
;**************************************************************
spurious:                           ; placed here so that security value
            NOP                     ; does not change all the time.
            RTI

;**************************************************************
;*                 Interrupt Vectors                          *
;**************************************************************
            ORG   $FFFA

            DC.W  spurious          ;
            DC.W  spurious          ; SWI
            DC.W  _Startup          ; Reset

 

 

We really don't know if we need interruptions or what.

 

 

Att,

Pedro Vinícius.

 

(Computer's Engineering student)

Labels (1)
0 Kudos
4 Replies

428 Views
rocco
Senior Contributor II

Hi Pedro,

 

Welcome to the forum.

 

I tried to look at your code, but I could not follow it, due to the lack of documentation.

 

Can I make a suggestion? You will find that the code is much easier to troubleshoot if it is readable. Here is an example of an ADC routine.

 

;
; AtoD driven by TBM interrupts, every 256 usec.
;       ; For TBM interrupts: ADIE=0, TBON=1
ADCinit: equ %00000000  ;COCO=0, ADIE=0, ADCO=0, no channel
TBMinit: equ %00010110  ;divider=8,192, TACK=0, TBIE=1, TBON=1
;       ; 32Mhz / 8,192 = 256 microseconds/sample
;
ADCclock: equ %01110000 ;select bus clock divided by 8
;
;
;
; The Analog to Digital Converter is used to read the
; potentiometers used for user interfacing. They could be
; speed pots, damping, menu access or whatever . . .
;
AtoDinit:
 mov #ADCclock,ADCLK  ;set the conversion clock
 clr ADCindex  ;set channel index to zero
 lda ADCselect  ;start conversion on channel 0
 sta ADSCR   ;load control register
;
; When we are using the Time-Base-Module for the ADC sample rate.
;
 mov #TBMinit,TBCR  ;start the Time Base Module
 rts    ;return initialized
;
;
;
; Interrupt Service routine for the A to D convertor.
;
AnalogInt:
 bset B.ACK,TBCR ;acknowledge the interrupt from the TBM
 lda ADR  ;read ADC to acknowledge its interrupt
 que AtoDprocess ;schedule latest sample to be processed
 rti   ;return from interrupt
;
;

Notice that the equates are separate from their use. So you can carefully examine the equates to verify they are correct, and then separately examine the code to see what it is doing with the data that you have already verified. You can then read the code without having to simultaneously decode the bits.

 

Notice, as an example the bit-set instruction. It is clear that you are setting the ACK bit, whereas a bit number would tell you nothing about what the instruction is trying to do. A comment would also help. This is the area where I had trouble.

 

Freescale has pre-defined all of the hardware registers and their bits in include files.

 

In any case, the above code will show you how I initialize and use the ADC in one application. I will try to find some PWM code.

0 Kudos

428 Views
Katsuke
Contributor I
; Include derivative-specific definitions            INCLUDE 'derivative.inc';; export symbols;            XDEF _Startup            ABSENTRY _Startup;; variable/data section;            ORG    RAMStart         ; Insert your data definition hereExampleVar: DS.B   1;; code section;            ORG    ROMStart_Startup:      LDA #%01100001 ; AIEN and ADCO enabled. ADCH = 0001, so the input selected is AD1,    ; which also uses PTA1/AD1 and the Pinc control ADPC1      STA ADCSC1                                                                             LDA APCTL1      BSET 2, APCTL1 ;Enable ADPC1 to disable I/O to became an analog input      LDA #%0100000 ; ADTRG = 1      STA ADCSC2       LDA #%00110100 ; Bus clock activated ADICLK = 00) , Mode = Continuous   ; long sample (ADLSMP = 1), ADIV = 01 (clock/2), ADLPC = 0      STA ADCCFG      LDA #%00001000  ; CPWMS = 0,  Bus rate clock (CLKSB:A = 0:1)      STA TPMSC      LDA #%00100000 ; PTB5 enabled as output      STA PTBDD      LDA #%00000000 ; All ports as inputs      STA PTADD       LDA #$02   ;Cop Disabled      STA SOPT1  ;Cop Disabled      LDHX   #RAMEnd+1        ; initialize the stack pointer      TXS      CLI                     ; enable interrupts        mainLoop:                   BSR RandW  ; Routine call      NOP     ; feed_watchdog        BRA    mainLoop  RandW:  ;function to read and write at the Channel 1 of the tpm      LDHX ADCRH  ; load converted value      STHX TPMC1V ; Store at PWM the converted value t be sent      RTS; EVERYTHING BELOW ISN'T BEING USED.;**************************************************************;* spurious - Spurious Interrupt Service Routine.             *;*             (unwanted interrupt)                           *;**************************************************************spurious:                           ; placed here so that security value            NOP                     ; does not change all the time.            RTI;**************************************************************;*                 Interrupt Vectors                          *;**************************************************************            ORG   $FFFA            DC.W  spurious          ;            DC.W  spurious          ; SWI            DC.W  _Startup          ; Reset

 

Does it got better?   Thanks for the attention!

 

 

Att,

Pedro V.

0 Kudos

428 Views
bigmac
Specialist III

Hello Pedro,

 

The TPM module associated with the HCS08 device should be capable of generating both 0 and 100 percent PWM duty.

 

The output from the ADC module will range 0-1023 decimal.  Assuming that the 16-bit value from ADCR is directly written to TPMC1V after each ADC conversion, you might set the TMOD value to 1023.  Assuming TPM prescale division of 1, the TPM period would be 1024 bus cycles, and the PWM duty range would be 0 to 1023/1024.

 

Alternatively, you could set TMOD to 1022, and the PWM duty should range 0 to 1 (i.e. 1023/1023).  In this case, the TPM period would be 1023 bus cycles.

 

For the ADC, I would not use continuous conversion mode, since there would appear to be little point in generating more than a single conversion for each PWM period.  Therefore, the ADC conversion should be synchronised to the PWM operation.

 

The concept is simple.  Wait for a PWM cycle to complete, by polling the TPM overflow flag.  Then read the result of the previous ADC conversion, and commence a new conversion.  Alternatively, the TPM overflow interrupt could be utilised.  The new duty value should then commence one PWM period later, after the next counter overflow.  The PWM period will be sufficient to complete the ADC conversion, without the need to test the COCO flag.

 

   

    ; Polling operation:

    brclr  TPMSC_TOF,TPMSC,*   ; Wait until overflow flag is set

    bclr   TPMSC_TOF,TPMSC     ; Clear overflow flag

    ldhx   ADCR                ; Read previous ADC conversion result

    sthx   TPMC1V              ; Update PWM value

    mov    #1,ADCSC1           ; Commence new conversion ADC ch 1

 

 

There are a number of other issues with your initialisation process.

 

Best to initialise the stack point prior to any other code.

No need for the LDA APCTL1 instruction prior to the BSET instruction.  However, the bit number is incorrect - for ADC channel 1 the bit number is 1 (and not 2).

The ADTRG bit within ADCSC2 should remain clear since you not externally triggering each conversion.

 

Regards,

Mac

 

   

0 Kudos

428 Views
rocco
Senior Contributor II

Hi Pedro,

 

Yes, that is much better.

 

I didn't have much time to look at it, but it appears you are setting-up the Timer Status and Control Register but not the channel register or the modulo register. Here is how I would do it, although there are other ways, as well.

 

Since the ADC is going to give you a value from 0 to 1023, I would set the timer's modulo to 1025. Then I would add 1 to the ADC value before loading into the PWM, so that it ranges from 1 to 1024 instead of 0 to 1023. That is because there are often problems associated with 0% and 100% duty-cycle. This way, the duty-cycle will range from approximately 0.1% to 99.9%, avoiding the problem values.

 

You need to set the channel register for your preferred PWM mode and set the modulo register to 1025. I would normally set the timer's clock and prescaler to be as fast as it can run (the Freescale PWMs are never fast enough for me), but that may not be necessary for your application.

 

How you synchronize your PWM updates will depend on what your trying to do.

0 Kudos