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)
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.
; 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.
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
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.