C to Assembly II

WBahn

Joined Mar 31, 2012
32,823
Where is the end of the while(1) loop? Either the while() loop or main() is not terminated (and, of course, the while() loop IS terminated by the last closing brace but the main() function is not).

If you don't need to do anything, then why are there four statements in it.

If those four statements are meant to be in the loop, then why are then indented beyond the while()'s level of indentation.

Proper indenting goes a LONG way toward making your code readable and maintainable.
 

philba

Joined Aug 17, 2017
959
Not much actual code there. I'd bet an asm version would not be much smaller than the compiled version. I'm pretty sure max just left off the last line (with the closing "}"). Easy to lose the last line with copy-n-paste.

By the way, that's Roman Black's code, I believe.
 
Last edited:

WBahn

Joined Mar 31, 2012
32,823
A half way competent compiler will optimize all of those down to the same machine code. Though the last one may not if x is assigned inside the while block or x is declared volatile or external. Though, if those are the only references (plus the declaration), any decent compiler will optimize away x. Heck, GCC does stuff like put variables into registers and completely eliminate the need for storage. Sure makes debugging confusing sometimes.
Compilers have long since made debugging fun. I remember the first time that it generated code that skipped code I had at the beginning of a while() loop and entered my loop block in the middle of the block without evaluating the test expression first. It took me the better part of an hour to figure out what it was doing. And this was before optimizing compilers made the jump into outright sorcery.
 

philba

Joined Aug 17, 2017
959
Yeah, was debugging some Attiny code yesterday and wanted to look at the stored value of a variable. No RAM, in a register. And f@ck!ng Atmel studio wouldn't show what register it was in. I guessed right but grrrr
 

Thread Starter

MaxHeadRoom

Joined Jul 18, 2013
30,654
Where is the end of the while(1) loop? Either the while() loop or main() is not terminated (and, of course, the while() loop IS terminated by the last closing brace but the main() function is not).
If you don't need to do anything, then why are there four statements in it.
If those four statements are meant to be in the loop, then why are then indented beyond the while()'s level of indentation.
Proper indenting goes a LONG way toward making your code readable and maintainable.
I assume the questions rhetorical?
As this is NOT my code, this is supposedly the C code for the Hex file posted, this downloaded from Roman Blacks site of whom does not seem to be around now to ask.
I came here with the question hoping to get the answer as I am not fluent in C.
Max.
.
 

WBahn

Joined Mar 31, 2012
32,823
I assume the questions rhetorical?
As this is NOT my code, this is supposedly the C code for the Hex file posted, this downloaded from Roman Blacks site of whom does not seem to be around now to ask.
I came here with the question hoping to get the answer as I am not fluent in C.
Max.
.
I didn't realize that it wasn't your code. I went back and followed the link in your prior post. Then assumed that it was referring to the link in the first post in that thread. On RB's page I saw the code for the interrupt, but couldn't find the main(). So I downloaded the zip file contained elsewhere on the page and finally found it. A lot of hoops to jump through.

The code as you posted is not the complete code -- you missed the final closing brace. Also, in posting it into the CODE block, the formatting got messed up. That's one thing that definitely annoys me about the CODE BB tag here -- I always have to go through line by line and clean up any code I post.
 

joeyd999

Joined Jun 6, 2011
6,279
OK here is the (very) short program.
Max.
C:
/******************************************************************************
  DC_motor_xtal.c  - run a DC motor at xtal "clockwork" locked speed

  PIC16F628A - 20MHz HS xtal (see web page for schematic)
  Compiler - MikroC for PIC v8
  PIC pins;
   RA0 = digital ST input, quad encoder A
   RA1 = digital ST input, quad encoder B
   RB3 = CCP1 output, PWM to motor, HI = ON
   RB0 = echo encoder A output
   RB1 = echo encoder A output

   PIC configs; MCRLE off, BODEN off, BORES off, WDT off, LVP off, PWRT on
******************************************************************************/


#define  MOTOR_PULSE_PERIOD  6944444    // 1 RPS
#define  MOTOR_PROP_GAIN  10

// global vars
unsigned char rpos;       // reference position of xtal based freq
unsigned char mpos;       // actual motor position
unsigned char mlag;       // amount the motor lags the reference
unsigned char enc_new;    // holds motor's quadrature encoder data for testing
unsigned char enc_last;
unsigned long bres;       // bresenham accumulator used to make ref frequency
unsigned char pins;       // temporary var for echoing encoder signals


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void interrupt()
{
  //-------------------------------------------------------
  // This is TMR0 int, prescaled at 2:1 so we get here every 512 instructions.
  // This int does the entire closed loop speed control;
  //  1. updates encoder to see if motor has moved, records it position
  //  2. updates reference freq generator, records its position
  //  3. compares the two, sets PWM if motor lags behind reference
  //  4. limit both counts, so they never roll, but still retain all error
  //-------------------------------------------------------
  // clear int flag straight away to give max safe time
  INTCON.T0IF = 0;

  //  1. updates encoder to see if motor has moved, records it position
  enc_new = (PORTA & 0b00000011);   // get the 2 encoder bits
  if(enc_new != enc_last)
  {
    if(enc_new.F1 != enc_last.F0) mpos++;   // record new motor position
    else                          mpos--;
    enc_last = enc_new;
  }

  //  2. updates reference freq generator, records its position
  bres += 102400;                 // add nS per interrupt period (512 insts * 200nS)
  if(bres >= MOTOR_PULSE_PERIOD)  // if reached a new reference step
  {
    bres -= MOTOR_PULSE_PERIOD;
    rpos++;                       // record new xtal-locked reference position
  }

  //  3. compares the two, set PWM% if motor lags behind reference
  if(mpos < rpos)   // if motor lagging behind
  {
    mlag = (rpos - mpos);                            // find how far it's lagging behind
    if(mlag >= (100/MOTOR_PROP_GAIN)) CCPR1L = 100;  // full power if is too far behind
    else CCPR1L = (mlag * MOTOR_PROP_GAIN);          // else adjust PWM if slightly behind
  }
  else            // else if motor is fast, cut all power!
  {
    CCPR1L = 0;
  }

  //  4. limit both counts, so they never roll, but still retain all error
  if(rpos>250 || mpos>250)
  {
    rpos -= 50;
    mpos -= 50;
  }

}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


//=============================================================================
//   MAIN
//=============================================================================
void main()
{
  //-------------------------------------------------------
  // setup PIC 16F628A
  CMCON = 0b00000111;     // comparators OFF, all pins digital

  PORTA = 0b00000000;     //
  TRISA = 0b00000011;     // RA0,RA1 used for digital ST encoder inputs

  PORTB = 0b00000000;     //
  TRISB = 0b00000000;     // RB3 is CCP1 PWM out, RB0,RB1 are encoder echo outputs

  // set TMR2 to roll every 100 ticks, PWM freq = 5mil/16/100 = 3125 Hz
  T2CON = 0b00000111;     // TMR2 ON, 16:1 prescale
  PR2 = (100-1);          // PWM total period of 100

  // set PWM out on CCP1
  CCPR1L = 0;             // PWM range 0-100% = start  with PWM of 0%
  CCP1CON = 0b00001100;   // CCP1 on, set to PWM mode

  // TMR0 used for interrupt, makes interrupt every 512 insts
  OPTION_REG = 0b10000000;   // PORTB pullups OFF, TMR0 on, 2:1 prescale


  //-------------------------------------------------------
  // setup before main loop
  Delay_mS(200);    // allow PSU voltages time to stabilise

  // setup vars etc, will be used in interrupt
  bres = 0;
  rpos = 0;
  mpos = 0;

  // finally start TMR0 roll interrupt
  INTCON = 0b10100000;  // GIE=on, TMR0IE=on

  //-------------------------------------------------------
  // main run loop
  while(1)
  {
    //-------------------------------------------------
    // We don't need to do anything in main loop as the motor speed
    // control is done entirely in the TMR0 interrupt.


    //-------------------------------------------------
    // For convenience in setting up the encoder trimpots,
    // echo the two encoder pins out two spare PORTB digital outputs!
    pins = 0;
    if(PORTA.F0) pins.F0 = 1;
    if(PORTA.F1) pins.F1 = 1;
    PORTB = pins;    // output those two pins only (PWM output is not affected)
  }

Arrrrrg! Why would you poll a rotary encoder inside a timer interrupt???

Use IOC to more properly manage your encoder.
 

joeyd999

Joined Jun 6, 2011
6,279

joeyd999

Joined Jun 6, 2011
6,279
I don't have a motor or encoder that would roll over faster than the TMR0 rollover.
Max.
Encoders can bounce between pulses quite quickly in certain instances, even when the motor is not running.

Do what you want. I'm just informing you that your implementation is not the correct technical approach.

I'll shut up now.
 

philba

Joined Aug 17, 2017
959
Encoders can bounce between pulses quite quickly in certain instances, even when the motor is not running.

Do what you want. I'm just informing you that your implementation is not the correct technical approach.

I'll shut up now.
Agreed on the encoder bounce. Would not interrupt on switch pins. I would use a timer interrupt at something like 5 or 10 mS period and sample the encoder switch pins so the interrupt handler is tiny. Once 3 or 4 consecutive samples are low, then the switch is low. Use a counter to count up to a max value when the switch pin is low and down to zero when it's high. Counter at max means switch is closed/not bouncing and not at max means it is open.
 

joeyd999

Joined Jun 6, 2011
6,279
Agreed on the encoder bounce. Would not interrupt on switch pins. I would use a timer interrupt at something like 5 or 10 mS period and sample the encoder switch pins so the interrupt handler is tiny. Once 3 or 4 consecutive samples are low, then the switch is low. Use a counter to count up to a max value when the switch pin is low and down to zero when it's high. Counter at max means switch is closed/not bouncing and not at max means it is open.
Debouncing is not necessary for encoders due to Gray code output.

https://forum.allaboutcircuits.com/...quadrature-rotary-encoders.64318/#post-440211
 

philba

Joined Aug 17, 2017
959
I think it's a bad idea to take interrupts from a pin that may bounce. You can get a flood of interrupts from bounces. If you must do that, then at least put an LFP on the pin(s).
 

WBahn

Joined Mar 31, 2012
32,823
I guess the proven method doesn't know that and works anyway.
Original OP question never really confirmed?.
Max.
I don't see any way to confirm it here. As many have pointed out, the compiler has several options in generating code that has the correct semantics. Which of these it will actually use depends on the specific compiler, the specific compiler version, the specific compiler options, and the target architecture.
 

Thread Starter

MaxHeadRoom

Joined Jul 18, 2013
30,654
I would have thought someone here would be familiar with MikroC?
And the target device was shown as 16F628a.
I will will go with my hunch and see what happens.
Max.
 

WBahn

Joined Mar 31, 2012
32,823
You asked specifically about the question in the original post, which is asking if a particular statement in C translates into a particular assembly language equivalent. I don't see anything in that post asking about the MikroC compiler or the 16F628a target. Even if that restriction is imposed, there is still the issue which version of the compiler you are using (the reason that there are versions is that the compiler changes over time and that includes the code generation algorithms) and the specific options you are using (which may or may not be the install defaults or the as-installed configuration chosen by others).

Is there something that is preventing you from compiling the code and then disassembling it to see what YOUR compiler is doing TODAY?

Many compilers (I don't know about MikroC in general or your IDE in particular, if you are using one) will let you see the object code fairly readily. If so, that should answer your question.
 

WBahn

Joined Mar 31, 2012
32,823
Now, it just occurs to me -- are you asking the question because you are trying to learn something about C (that was the impression I had), or are you just trying to understand the language semantics well enough to implement the same logic in assembly without concern how the compiler that RB used actually did it?

If the latter, then what you have should be equivalent. You are just staying in an infinite loop and letting interrupt service routines do their thing forever.
 

AlbertHall

Joined Jun 4, 2014
12,625
I think it's a bad idea to take interrupts from a pin that may bounce. You can get a flood of interrupts from bounces. If you must do that, then at least put an LFP on the pin(s).
The interrupt is from a timer not the encoder designed to be faster than the encoder at the maximum motor speed. During the interrupt the encoder is read. If the motor is stopped and one of the quadrature sensors is close to the switch point and its output bounces then the sensed position will bobble up and down by one count. This doesn't matter. The purpose is to drive the motor at a constant average speed.
 
Top