## Fast dividing by 5 and by 10 for uint32_t input data for ATMEGA16A

123 posts / 0 new

## Pages

The "{{{{" go to the nested if checks in the thing to trim leading zeros; each of those lines has a { but no }. Eww.

Having looked at the various forms of this, I think that optimizing division by ten is not nearly as useful as doing division by 100, and then having a lookup table for %100 to pairs of digits. Of course, you still have to make the division by 100 faster, but 100 bytes of flash for hex-packed lookup tables (or 200 for ASCII lookup tables) seems pretty decent. So, you just have a uint8_t in flash, and [69*2] is 0x36, and [69*2+1] is 0x39, and hey, there you go.

rpz3598 wrote:

```  while (R >=0x2710){ Q++;  R-=0x2710; }
digits= (uint8_t) Q +0x30;
Q = 0;
while (R >=0x03e8 ){ Q++; R-=0x03e8 ; }
digits = (uint8_t) Q +0x30;
Q = 0;
while (R >=0x0064) { Q++; R -=0x0064;}
digits = (uint8_t) Q +0x30;
Q = 0;
while (R >=0x000A  ) { Q++; R -=0x000A ;  }
digits = (uint8_t) Q +0x30;
digits=  (uint8_t) R +0x30;```

Is this an obsfucation contest entry? Why would you give the literal constants in hex (0x2710, 0x3E8, 0x64, 0x0A) ? Most readers would be happier seeing 10000, 1000, 100, 10 wouldn't they for a decimal conversion? By the same token why use 0x30 when '0' tells the reader much more about the intention of the code. I also realise that Q and R are quotient and remainder but why limit to single letter variables?

```  while (remainder >= 10000) {
quotient++;
remainder -= 10000;
}
digits = (uint8_t) quotient + '0';
quotient = 0;
while (remainder >= 1000 ) {
quotient++;
remainder -= 1000;
}
digits = (uint8_t) quotient + '0';
quotient = 0;
while (remainder >= 100) {
quotient++;
remainder -= 100;
}
digits = (uint8_t) quotient + '0';
quotient = 0;
while (remainder >= 10  ) {
quotient++;
remainder -= 10;
}
digits = (uint8_t) quotient + '0';
digits = (uint8_t) remainder + '0';```

is exactly the same code, produces the exact same opcodes but is much more readable for the maintainer.

Alternative editions of the ATMEGA8A DDS  with TV (som problems with timer 0, timer2 noise , you can rebuild it for ATMEGA16A). Using DDS Soir compatible pinouts and circuit (alt building).

## Attachment(s): vm6_tvsoircompat.zip

How to add PWM (menu mode 8 ) with frequncy , DCy (0-99%), Order (1-6)  control for OC1A?

```
/*
1 set Order
2 set freq
3 set DCy
4 nothing , as if  mod1= 0
*/
uint8_t mod1=0;
uint8_t Order=0;

void SelectPWMDutyCycle();

void IncOrderPWM(){
Order++;  //fix if order =0 , then order =1 or off
if(Order>6){ Order=1;}
if(Order==0){ Order=1;}
}

void DecOrderPWM(){
if(Order==0){ Order=1; }
Order--;  //fix if order =0 , then order =1 or off
if(Order<1){ Order=6; }
}

void IncFreqPWM();   //insert code
void DecFreqPWM();  //insert code

void IncDCyPWM();    //insert code
void DecDCyPWM();   //insert code

{
if (mod1==0) {  PWMBlinkOff();  return; }
if (mod1==1){   SelectPWMFreqOrderBlink(); return ;  } //1...6  (1-1...10Hz, 2 -10...100 Hz, ... 6-100...1000 kHz  by Up,Down )
if (mod1==2){   SelectPWMHSFreqBlink(); return ; }  //(1,2...10  step 1 , 1 after 10 )*10^Order  by Up,Down
if (mod1==3){   SelectPWMDutyCycle(); return ; } //0-99% , step 1%  by Up,Down
}

void OnButtonRightPWM()
{
if(mod1==0){ mod1++ ; }
if(mod1>=4){ mod1=0 ;  return; }
}

void OnButtonLeftPWM()
{
if(mod1==0){ mod1=3 ;  return; } //fix
if(mod1>=1){ mod1-- ; }
}

void  OnButtonUpPWM()
{
if(mod1==1) { IncOrderPWM(); return ; }
if(mod1==2) { IncFreqPWM(); return ; }
if(mod1==3) { IncDCyPWM(); return ; }
return;
}

void  OnButtonDownPWM()
{
if(mod1==1) { DecOrderPWM(); return ; }
if(mod1==2) { DecFreqPWM(); return ; }
if(mod1==3) { DecDCyPWM(); return ; }
return;
}

/*
...
//if  right button pressed
if (mode==8) {  OnButtonRightPWM();   /*...*/  return; }  //fix
...
//
//if  left button pressed
if (mode==8) {  OnButtonLeftPWM();   /*...*/  return; }  //fix
...

//if Up button pressed
if (mode==8) {  OnButtonUpPWM();   /*...*/  return; }  // fix

...
//if Down button pressed
if (mode==8) {  OnButtonDownPWM();   /*...*/  return; }  // fix
...
*/```

For ATMEGA8A may be  memory overflow (if not use assembler).

Last Edited: Wed. Jun 16, 2021 - 03:29 PM
```
#define F_CPU  16000000UL

#include <stdio.h>
#include <stdlib.h>
//#include <stdbool.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>
// #include "lcd_lib.h"
#include <math.h>

inline void pwm_stop()
{
TCCR1A = 0;
TCCR1B = 0;
PORTB &= ~(1<<PB1);
DDRB&= ~(1<<PB1);
}

/*

uint32_t  div_const( uint32_t N , uint32_t D  )
{
uint32_t Q=0;
uint32_t R=F_CPU ; //(uint32_t )N;      //fix
while(R>=D){ Q++; R-=D; }
//printf ("\nN=%ld R=%ld  Q=%ld  D=%ld",N,R,Q,D);

return  R;
}

*/
uint32_t  div_const(   uint32_t D  )
{
uint32_t Q=0;
uint32_t R=16000000UL ; //(uint32_t )N;      //fix
while(R>=D ){Q++;  R-=D ; }
//printf ("\nN=%ld R=%ld  Q=%ld  D=%ld",N,R,Q,D);

return  (uint32_t)Q;
}

uint32_t  div_25( uint32_t N   )
{
uint32_t Q=0;
uint32_t R=(uint32_t ) N;
while(R>=25UL){ Q++; R-=25UL; }
//printf ("\nN=%ld R=%ld  Q=%ld  ",N,R,Q );
return  (uint32_t)Q;
}

void pwm_start(uint32_t freq, uint8_t dcy)
{
uint32_t period;
PORTB &= ~(1<<PB1);
DDRB |= (1<<PB1);
period =(uint32_t)div_const((uint32_t) freq)-1;  // (F_CPU/freq) - 1;  //fix    15999
TCCR1A = (1<<COM1A1) |(1<<WGM11);
//fast PWM

/*
WGM mode 14 = Fast PWM mode :
fOC1APWM = fclk=IO / ( N * (1 + TOP))
N = 1 - prescaler
TOP = ICR1 = 532.

*/

if(freq > 130)
{
ICR1 =  (uint16_t)period;
//if(SG.mode==6){ OCR1A =  (uint16_t)(period/2); }  //fix
//else /*if(SG.mode==8) */  {
//OCR1A = (uint16_t)(period * dcy / 100);     //15999*50/100 7999
//OCR1A =  (uint16_t)(ICR1 /2);
OCR1A =(uint16_t)div_25(((uint32_t)(ICR1 * dcy) >>2) );
//}
TCCR1B =(1<<WGM13)  | (1<<WGM12) |(1<<CS10); //??? ????????
}//fast PWM, mode 14, prescaler=1
else
{
period =period>> 8;
ICR1 = (uint16_t) period;
//  OCR1A = (uint16_t)(period * dcy / 100);
OCR1A =(uint16_t)div_25(((uint32_t)(ICR1 * dcy)>>2) );
TCCR1B =(1<<WGM13)  | (1<<WGM12) | (1<<CS12); // ???????? 256
}//fast PWM, mode 14, prescaler=256

}

/*********************/

//const uint8_t welcomeln1[] PROGMEM ="AVR LCD DEMO\0";
int main(void)
{
//Initialize

// Timer1_StartPWMFastPWM14Icr();
pwm_start((uint32_t)30000,50)  ;  //works if ICR1=532

while(1){ ; }

return 0;

}

```

How to rebuild for freq=1Hz(1000Hz)...100000 Hz?

Works is ICR=532 (f=30000 Hz) , if 20 kHz, 40 kHz, 50 kHz  problem with duty cycle.

```#define F_CPU  16000000UL

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>
//#include "lcd_lib.h"
#include <math.h>

void pwmInit (void)
{
PORTB &= ~(1<<PB1);
DDRB |= (1<<PB1);

TCCR1B = (1 << CS11);
//TOP= 32:
ICR1 = 32;
// for DCy=50%
OCR1A = 16;
//OC1A = 0 if  TCNT1= OCR1A:
TCCR1A = (1 << COM1A1);
// Phase and Frequency Correct, mode 8
TCCR1B |= (1 << WGM13);
//Iano?ieea ii?ee OC1A eae auoia:
//DDRB |= (1 << PB1);
}

/*********************/

//const uint8_t welcomeln1[] PROGMEM ="AVR LCD DEMO\0";
int main(void)
{
//Initialize

// Timer1_StartPWMFastPWM14Icr();
//  pwm_start((uint32_t)80000,50)  ;
pwmInit ()   ;
while(1){ ; }

return 0;

}```

Is this program correct? Is OCR1A  and ICR1  8 bit in the  mode 8 (ICR) and mode 9(OCR1A) (for problem in the VMLAB for mode 8 , mode 9 of WGM  for ATMEGA8 )?

How to create program for  PWM control for F=1Hz(1kHz)-100 kHz , DutyCycle=1..99% (with DCy error < +/- 1 % , for F=16 MHZ)?

In the VMLAB 3.15 no signal  on the PB1 .

## Attachment(s): timer1.zip VM-LAB.rar vmpwm.zip

WOW this must be the slowest (3 months) divide code ever! John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

but thidis a compact code . May be more optimal . Some codes for SimulIDE (problem    with OC1A        in the SimulIDE_0.4.14-SR4_Win32, fixed in the     SimulIDE_0.4.15-SR1_Win64  , but with some bugs    , fixed problem with diodes  )

## Attachment(s): simulide_DDS.zip
Last Edited: Sat. Jun 19, 2021 - 09:20 AM

My programs of the DDS    with application of some of the  subroutines  ,models for  SimulIDE_0.4.15-SR1_Win64 (alt builds for Soir's  DDS ( https://sxem.org/2-vse-stati/24-... ) ).   May be with some bugs, but works .

## Attachment(s): DDSworks.zip
```

void Timer1_Start(uint8_t FkHz)
{

DDRB|=0b00000010;

//TCCR1A=0x40;  //Output compare toggles OC1A pin
//  WGM13=0, WGM12=1; CS12 =0 CS11=0,CS10=1 (N=1)
/*
if (FkHz==0)
{

//mode 12 , CTC,ICR , no toogles, DCy=50%
ICR1=1;
OCR1A=0;
TCCR1A = (1<<COM1A1) |(0<<WGM11);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
}
else
{*/
//mode 4 CTC, toggle

TCCR1A = (0<<COM1A0)|(1<<COM1A0) | (0<<WGM11)|(0<<WGM10)    ;
if(FkHz>=HSWITHPRESC1) {  TCCR1B= (1<<WGM12)| (1<<CS11); } else {  TCCR1B= (1<<WGM12)| (1<<CS10);  }
//}

}

void Timer1_Stop(void){ TCCR1B=0x00;  TCCR1A =0x00;            } //timer off```

works on 8 MHz (OCR1A=0) in toogle mode  with ATMEGA8A , but problem in the emulator (ATMEAGA8), gets zero .

My program for ATMEGA8A , works   . For Atmel Studio 7

ALT+F7(project)  proj. name Properties ->Toolchain->AVR/GNU Linker - > add segments -> Flash segments

MySection1=0x0D00
MySection2=0x0D80
MySection3=0x0E00
MySection4=0x0E80
MySection5=0x0F00
MySection6=0x0F80

## Attachment(s): vm105exp.zip dds8app105exp.zip
```

/*
void  inline   Parser1( uint16_t  period,  uint8_t dcy   )
{
uint16_t res1=0;
asm volatile(

"1:  mov r16, %A1 ; L" "\n\t"
" mov r17,   %B1 ; H"  "\n\t"

" mov r13, %2 " "\n\t"
" clr r20 ; mul  r17, r14;  ; ah * bh ;" "\n\t"
" clr r21 ; movw  r20,  r0  " "\n\t"
" mul  r16, r13   ; al * bl; " "\n\t"
" movw r18,  r0  " "\n\t"
" mul  r17, r13   ; ah * bl; " "\n\t"
" clr  r13" "\n\t"
" add  r19, r0 " "\n\t"
" adc  r20, r1 " "\n\t"
" adc  r21, r13 " "\n\t"

"clr   R26 " "\n\t"

"clr r25 ; begin dividing by 100 sub " "\n\t"
"ldi r24, 0x64 ;" "\n\t"

"clr   R16  " "\n\t"
"clr   R17 " "\n\t"
"clr   r22 " "\n\t"
"clr   r23 " "\n\t"

"2: clc" "\n\t"
" cp    R18, R24   ; if {r21:r20 ; r19:18} < 100 , exit  " "\n\t"
"cpc   R19, R25  " "\n\t"
"cpc   R20, R25  " "\n\t"
"cpc   R21, R25  " "\n\t"
" brlo    3f" "\n\t"

"clc" "\n\t"
"ldi   r22, 1 "  "\n\t"
"add   R16, R22 ; " "\n\t"
"adc   R17, R23 ; Result++ " "\n\t"
"clc" "\n\t"

"sub   R18, R24 ; R-=100 " "\n\t"
"sbc   R19, R25 ; " "\n\t"
"sbc   R20, R25 "   "\n\t"
"sbc   R21, R25 "   "\n\t"
"clc" "\n\t"
"rjmp  2b " "\n\t"

"3: " "\n\t"
"clc" "\n\t"
"  mov %A0, r16; " "\n\t"
" mov %B0,  r17; " "\n\t"

:  "=r"(res1)
:  "r"(period),"r"(dcy)

);

OCR1A=res1;
return  ;
}

*/

inline void pwm_stop(void){ TCCR1A = 0; TCCR1B = 0; HSPORT &= ~(1<<HS ); HSDDR &= ~(1<<HS) ; }

void pwm_start1(uint8_t freq, uint8_t ord, uint8_t dcy)
{
HSPORT  &= ~(1<<HS);
HSDDR |= 1<<HS;
TCCR1A = (1<<COM1A1) |(1<<WGM11);

if(ord==0) { ICR1=(uint16_t)pgm_read_word(PWM_0ICR+(uint16_t) freq ) ;  }
if(ord==1) { ICR1=(uint16_t)pgm_read_word(PWM_1ICR+(uint16_t) freq ) ; }
if(ord==2) { ICR1=(uint16_t)pgm_read_word(PWM_2ICR+(uint16_t) freq ) ; }
if(ord==3) { ICR1=(uint16_t)pgm_read_word(PWM_3ICR+(uint16_t) freq ) ; }
if(ord==4) { ICR1=(uint16_t)pgm_read_word(PWM_4ICR+(uint16_t) freq ) ; }
if(ord==5) { ICR1=(uint16_t)pgm_read_word(PWM_5ICR+(uint16_t) freq ) ; }
//ICR1=53332;

// Parser1( ICR1,  dcy ); //=(dcy*period)/100+
//uint32_t period= (ICR1*dcy);
//period/=100;

dcy=50;
/*
int32_t R=(int32_t)((uint16_t)ICR1*(uint8_t)dcy);

if((dcy&0x01)!=0) {  R+= ( uint32_t)(ICR1<<0) ; }
if((dcy&0x02)!=0) {  R+= ( uint32_t)(ICR1<<1) ; }
if((dcy&0x04)!=0) {  R+= ( uint32_t)(ICR1<<2) ; }
if((dcy&0x08)!=0) {  R+= ( uint32_t)(ICR1<<3) ; }

if((dcy&0x10)!=0) {  R += ( uint32_t)(ICR1<<4) ; }
if((dcy&0x20)!=0) {  R+= ( uint32_t)(ICR1<< 5 ); }
if((dcy&0x40)!=0) {  R += ( uint32_t)(ICR1<<6) ; }
if((dcy&0x80)!=0) {  R += ( uint32_t)(ICR1<<7) ; }

uint16_t Q1=0;
while((int32_t)R >=0x00000064 ){ Q1++;   R -=0x00000064   ; }
OCR1A=Q1;
*/
OCR1A=(uint16_t)(ICR1>>1);

if((ord<2)||((ord==2)&&(freq<2)))
{
//period /= 256;
TCCR1B =(1<<WGM13)  | (1<<WGM12) | (1<<CS12);// N=256
}
else
{
TCCR1B =(1<<WGM13)  | (1<<WGM12) |(1<<CS10);//N=1
}
}
```

Problem with registers after turn on and OCR1A obtaining : data mapping error (for example   for 300 kHz , DCy error  for 50 % ), temporary DCy set to 50 %

Problem may be fixed if  use only assembler and manually define  registers  and stack .

## Attachment(s): vm105exp1err.zip
```while((int32_t)R ...   //may be with carry error ,.
//if uint32_t  and with error , if store this data in the registers and problem
//use assembler ```

But code with asm equivalent of the

```uint16_t Q1=0;
while((int32_t)R >=0x00000064 ){ Q1++;   R -=0x00000064   ; }
OCR1A=Q1;```

may be compact and simple (if fic carry problem for long types ) .

Test program for dividind . If I use it  in the hsgen.h,  problem with stack, registry mappings, push/pop is not fixed.

## Attachment(s): testdiv.zip
```
void  static inline   Parser1( uint16_t  period,  uint8_t dcy   )
{

//ICR1=period;
uint16_t res1=0;
asm volatile(

"1:   push r16" "\n\t"
"    push r17" "\n\t"
"  in r16, __SREG__" "\n\t"
"  push r16" "\n\t"
"     push r18" "\n\t"
"    push r19" "\n\t"
"     push r20" "\n\t"
"     push r21" "\n\t"
"     push r0" "\n\t"
"     push r1" "\n\t"

" mov r16, %A1 ; L" "\n\t"
" mov r17,   %B1 ; H"  "\n\t"

" mov r13, %2 " "\n\t"
" clr r20 ; mul  r17, r14;  ; ah * bh ;" "\n\t"
" clr r21 ; movw  r20,  r0  " "\n\t"
" mul  r16, r13   ; al * bl; " "\n\t"
" movw r18,  r0  " "\n\t"
" mul  r17, r13   ; ah * bl; " "\n\t"

" clr  r13" "\n\t"
" add  r19, r0 " "\n\t"
" adc  r20, r1 " "\n\t"
" adc  r21, r13 " "\n\t"

"clr r13 ; begin dividing by 100 sub " "\n\t"

"clr   R16  " "\n\t"
"clr   R17 " "\n\t"

"2: clc" "\n\t"
" cpi    R18, 0x64  ; if {r21:r20 ; r19:18} < 100 , exit  " "\n\t"
"cpc   R19, R13  " "\n\t"
"cpc   R20, R13  " "\n\t"
"cpc   R21, R13  " "\n\t"
" brlo    3f" "\n\t"

"sec" "\n\t"
"adc   R16, R13 ; " "\n\t"
"adc   R17, R13 ; Result++ " "\n\t"
"clc" "\n\t"

"subi   R18, 0x64 ; R-=100 " "\n\t"
"sbci   R19, 0x00 ; " "\n\t"
"sbci   R20, 0x00 "   "\n\t"
"sbci   R21, 0x00 "   "\n\t"
"clc" "\n\t"
"rjmp  2b " "\n\t"

"3: " "\n\t"
"clc" "\n\t"
"   mov %A0, r16; " "\n\t"
"  mov %B0,  r17; " "\n\t"

" pop r1" "\n\t"
" pop r0" "\n\t"
" pop r21" "\n\t"
"pop r20" "\n\t"
"pop r19" "\n\t"
"pop r18" "\n\t"
" pop r16" "\n\t"
" out __SREG__, r16" "\n\t"
"pop r17" "\n\t"
"pop r16" "\n\t"

:   "=r"(res1)
:  "r"(period),"r"(dcy)
: "r0","r1","r13", "r16","r17","r18","r19","r20","r21",  "cc","memory"

);
OCR1A=res1;
// uint8_t i=0;

return  ;
}

inline void pwm_stop(void){ TCCR1A = 0; TCCR1B = 0; HSPORT &= ~(1<<HS ); HSDDR &= ~(1<<HS) ; }

void pwm_start1(uint8_t freq, uint8_t ord, uint8_t dcy)
{
HSPORT  &= ~(1<<HS);
HSDDR |= 1<<HS;
TCCR1A = (1<<COM1A1) |(1<<WGM11);

if(ord==0) { ICR1=(uint16_t)pgm_read_word(PWM_0ICR+(uint16_t) freq ) ;  }
if(ord==1) { ICR1=(uint16_t)pgm_read_word(PWM_1ICR+(uint16_t) freq ) ; }
if(ord==2) { ICR1=(uint16_t)pgm_read_word(PWM_2ICR+(uint16_t) freq ) ; }
if(ord==3) { ICR1=(uint16_t)pgm_read_word(PWM_3ICR+(uint16_t) freq ) ; }
if(ord==4) { ICR1=(uint16_t)pgm_read_word(PWM_4ICR+(uint16_t) freq ) ; }
if(ord==5) { ICR1=(uint16_t)pgm_read_word(PWM_5ICR+(uint16_t) freq ) ; }
//ICR1=53332;

Parser1( ICR1,  dcy );

if((ord<2)||((ord==2)&&(freq<2)))
{
//period /= 256;
TCCR1B =(1<<WGM13)  | (1<<WGM12) | (1<<CS12);// N=256
}
else
{
TCCR1B =(1<<WGM13)  | (1<<WGM12) |(1<<CS10);//N=1
}
}
```

Fixed (stack, memory clobbering string ), but may be some problem with stack .

Exampe in the attach (may be with some  bugs , problem of  DCy tolerance ( rounding OCR1A, normal for this  processor) for some frequencies  ).

## Attachment(s): vm105exp1fixed.zip

But problem is not fixed  in the real ATMEGA8A-PU ( "-" -problem , if DCy=50%  )

```
/*

//period=(F_CPU/freq) - 1
//ICR1 = (uint16_t)period;
//    if(freq > 130) period /= 256;  change  prescaler
// OCR1A=  (uint16_t)(period * d / 100);
5
1000000	15	+
900000	16.7 	+
800000	19	+
700000	21.8 	+
600000	25.6          +
500000	31	+
400000	39	+
300000	52.3           +
200000	79	+
100000	159            -
159,79,52,39,31,25,21,19,17,15

100,200,300,400,500,600,700,800,900,1000 kHz
4
100000	159	-
90000	176.7        -
80000	199	-
70000	227.5 	 -
60000	265.6 	-
50000	319	-
40000	399	-
30000	532.3 	-
20000	799	-
10000	1599         -
1599,799,532,399,319,265,227,199,176,159

10,20,30,40,50,60,70,80,90,100 kHz
3

10000	1599	-
9000	1776.7 	-
8000	1999	-
7000	2284.7 	-
6000	2665.6 	-
5000	3199	-
4000	3999	-
3000	5332.3 	-
2000	7999	-
1000	15999	-
15999,7999,5332,3999,3199,2665,2285,1999,1777,1599

1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 kHz

2

1000	15999	;62.5 - for period/256
900	17776.7 	;69.4-
800	19999	;78.1-
700	22856.1 	;89.2-
600	26665.6 	;104.1-
500	31999	;124.9-
400	39999	;156.2-
300	53332.3 	;208.3-
200	79999	;312.4 // use period/256+
100	159999	;624.9 // use period/256+

624,312,   53332,39999,31999,26665,22856,19999,17777,15999

100,200,300,400,500,600,700,800,900,1000 Hz

1
100	159999	;624.9 +// use period/256
90	177776.7 	;694.4+
80	199999	;781.2+
70	228570.4 	;892.8-
60	266665.6 	;1041.6-
50	319999	;1249.9-
40	399999	;1562.49-
30	533332.3 	;2083.3-
20	799999	;3124.9-
10	1599999	;6249.9-
6249,3125,2083,1562,1250,1041,893,781,694,624

10,20,30,40,50,60,70,80,90,100 Hz

0
10	1599999	;6249.9-
9	1777776.7  ;6944.4;-
8	1999999	 ;7812.49-
7	2285713.2 ;8928.56-
6	2666665.6;10416.6;-
5	3199999	;12499.9;-
4	3999999	;15624.9;-
3	5333332.3 ; 20833.3-
2	7999999	;31249.9-
1	15999999	;62499.9;-

62500,31250,20833,15625,12450,10416,8928,7812,6944,6249

1,2,3,4,5,6,7,8,9,10 Hz
*/
```

```

void  static inline   Parser1( uint16_t  period,  uint8_t dcy   )
{

asm volatile(

"1:   push r16" "\n\t"
"    push r17" "\n\t"
"  in r16, __SREG__" "\n\t"
"  push r16" "\n\t"
"     push r18" "\n\t"
"    push r19" "\n\t"
"     push r20" "\n\t"
"     push r21" "\n\t"
"     push r0" "\n\t"
"     push r1" "\n\t"
"     push r13" "\n\t"
"     push r12" "\n\t"

" mov r16, %A0 ; L" "\n\t"
" mov r17, %B0 ; H"  "\n\t"

"   out %3, r17; " "\n\t"
"   out %2, r16; " "\n\t"

" mov r13, %1 " "\n\t"
" clr r20 ; mul  r17, r14;  ; ah * bh ;" "\n\t"
" clr r21 ; movw  r20,  r0  " "\n\t"
" mul  r16, r13   ; al * bl; " "\n\t"
" movw r18,  r0  " "\n\t"
" mul  r17, r13   ; ah * bl; " "\n\t"

" clr  r13" "\n\t"
" add  r19, r0 " "\n\t"
" adc  r20, r1 " "\n\t"
" adc  r21, r13 " "\n\t"

"clr r13 ; begin dividing by 100 sub " "\n\t"
"ldi r16,0x01  " "\n\t"
"mov r12,r16  " "\n\t"
"clr   R16  " "\n\t"
"clr   R17 " "\n\t"

"2: clc" "\n\t"
"clr r13" "\n\t"
" cpi   R18, 0x64  ; if {r21:r20 ; r19:18} < 100 , exit  " "\n\t"
"cpc   R19, R13  " "\n\t"
"cpc   R20, R13  " "\n\t"
"cpc   R21, R13  " "\n\t"
" brlo    3f" "\n\t"

"clc" "\n\t"
"adc   R17, r13 ; " "\n\t"
"clc" "\n\t"

"subi   R18, 0x64 ; R-=100 " "\n\t"
"sbci   R19, 0x00 ; " "\n\t"
"sbci   R20, 0x00 "   "\n\t"
"sbci   R21, 0x00 "   "\n\t"
"clc" "\n\t"
"rjmp  2b " "\n\t"

"3: " "\n\t"
"clc" "\n\t"

"   out %5, r17; " "\n\t"
"   out %4, r16; " "\n\t"

" pop r12" "\n\t"
" pop r13" "\n\t"
" pop r1" "\n\t"
" pop r0" "\n\t"
" pop r21" "\n\t"
"pop r20" "\n\t"
"pop r19" "\n\t"
"pop r18" "\n\t"
" pop r16" "\n\t"
" out __SREG__, r16" "\n\t"
"pop r17" "\n\t"
"pop r16" "\n\t"

:
: "r0","r1","r12","r13"  ,"r16","r17","r18","r19","r20","r21" , "cc","memory"

);
// uint8_t i=0;

return  ;
}

#define HSPORT PORTB
#define HS PB1
#define HSDDR DDRB
void pwm_start1(uint8_t freq, uint8_t ord, uint8_t dcy)
{
HSPORT  &= ~(1<<HS);
HSDDR |= 1<<HS;
TCCR1A = (1<<COM1A1) |(1<<WGM11);
uint16_t periodval=0x0001;
if(ord==0) { periodval=(uint16_t)pgm_read_word(PWM_0ICR+(uint16_t) freq ) ;  }
if(ord==1) { periodval=(uint16_t)pgm_read_word(PWM_1ICR+(uint16_t) freq ) ; }
if(ord==2) { periodval=(uint16_t)pgm_read_word(PWM_2ICR+(uint16_t) freq ) ; }
if(ord==3) { periodval=(uint16_t)pgm_read_word(PWM_3ICR+(uint16_t) freq ) ; }
if(ord==4) { periodval=(uint16_t)pgm_read_word(PWM_4ICR+(uint16_t) freq ) ; }
if(ord==5) { periodval=(uint16_t)pgm_read_word(PWM_5ICR+(uint16_t) freq ) ; }
//periodval=0x000F;

Parser1( periodval,  dcy ); //=(dcy*period)/100+

if((ord<2)||((ord==2)&&(freq<2)))
{
//period /= 256;
TCCR1B =(1<<WGM13)  | (1<<WGM12) | (1<<CS12);// N=256
}
else
{
TCCR1B =(1<<WGM13)  | (1<<WGM12) |(1<<CS10);//N=1
}
}
```

program for debug . Fixed problem with inc counter  . Fix problem with coefficients and prescaler values .

## Attachment(s): vmpwm1.zip

Test   version (alt. build) for ATMEGA8A(source  circuit from https://sxem.org/2-vse-stati/24-... with patches  ) with PWM (1-99%, mode 15 3*5 frequencies),  HF out, exponential, sinus, triangle, sawtooth, rev. sawtooth, square signals .

## Attachment(s): dds8app105exp50_mod152.zip

I'm honestly very confused by all this PWM stuff and don't see what it has to do with the "fast divide by 5 or 10" question.

``` #include <stdio.h>
#include <stdint.h>
#include <math.h>
/*
uint16_t  divs1000b(uint32_t n) {
uint32_t  q, r, t;

n = n + (n>>31 & 999);
t = (n >> 7) + (n >> 8) + (n >> 12);
q = (n >> 1) + t + (n >> 15) + (t >> 11) + (t >> 14) + (n >> 26) + (t >> 21);
q = q >> 9;
r = n - q*1000;
return (uint16_t )     (q + ((r + 24) >> 10));
// return q + (r > 999);

}
*/

//707=0b 0000 0010 1100 0011
//n =n+(n<<1)+(n<<6)+(n<<7)+(n<<9);

//n=(uint32_t) (n>>3) - (n>>5) + (n>>7) - (n>>9) + (n>>11) - (n>>13) + (n>>15)  - (n>>17) + (n>>19) - (n>>21) + (n>>23) - (n>25) + (n>>27)  - (n>>29) + (n>>31);
//n=(uint32_t) (n>>3) - (n>>5) + (n>>7) - (n>>9) + (n>>11) - (n>>13) + (n>>15)  - (n>>17) + (n>>19) - (n>>21) + (n>>23) - (n>25) + (n>>27)  - (n>>29) + (n>>31);
//q=(uint32_t) (n>>3) - (n>>5) + (n>>7) - (n>>9) + (n>>11) - (n>>13) + (n>>15)  - (n>>17) + (n>>19) - (n>>21) + (n>>23) - (n>25) + (n>>27)  - (n>>29) + (n>>31);

// q=(uint32_t)(((uint32_t)n * (uint32_t)0xCCCD) >> 16) >> 3;
// q=(uint32_t) (((uint32_t)q * (uint32_t)0xCCCD) >> 16) >> 3;
//  q=(((uint32_t)q * (uint32_t)0xCCCD) >> 16) >> 3;

uint16_t  mul707_divu1000b(uint32_t n) {
uint32_t q, r, t;
//n*=707
t=(uint32_t)n+(n<<1);  //temporary
n=(uint32_t)t+(t<<6)+(n<<9); //3n+(3n<<6)+(n<<9)

// return (( n>>2) + ( n>>8) + (( n>>1) + ( n>>5) + ( n>>8) + ( n>>11) + ( n>>12) + ( n >>14) + ( n >> 15)) >> 8) >>8  ;

// q=(uint32_t) ((((uint32_t)n * (uint32_t)0xCCCD) >> 16) >> 3);
// q=(uint32_t) (((uint32_t)q * (uint32_t)0xCCCD) >> 16) >> 3;
// q=(((uint32_t)q * (uint32_t)0xCCCD) >> 16) >> 3;

//   return (uint16_t )q  ;//result
//dividing by 1000
//}

// t = (n >> 7) + (n >> 8) + (n >> 12);
t=(n>>7) ; //7
t+=(t>>1)+(t>>5); //n>>8 + n>>12
//t+=(n>>12);//12   =(n>>7)>>5
q = (n>>1)+t+(n>>15)+(t>>11)+(t>>14);
q = q>>9;
//r=n-q*1000   //n+q*24-q*1024
//return q + ((r + 24) >> 10);
r=(uint32_t)n+24   ;
q<<=3;  //q*8        6
r+=q+q+q; //3*q*8=q*24
q<<=6; //<<10      6
r-=q; //(q<<10); //3+1+6
q+=r ;   //q
q>>=10;
return (uint16_t )q  ;//result
}
//0011 1110 1000
//1000

//a=(x<<3)+(x<<5)+(x<<6)+ (x<<7)+ (x<<8)+ (x<<9)

int main() {
int n;
uint32_t n1;
uint16_t q1;

for (int n2=0;n2<=1023;n2++)
{
q1 = mul707_divu1000b(n2);
printf("\n %ld n1=%ld  q1=%ld",n2,(int)707*n2,(int)q1  );
if(q1!=(uint16_t)(707*n2/1000  ))  {printf(" *******"); }

}

return 0;
}```

Scalling result =0.707*ADCW (for pi16f676 or other processors, for example ).

Modified  code from  Hacker's Delight 2nd Edition by Henry S Warren, Jr.

Prototype

```/* Code below uses 1/1000 = 0.0000 0000 0100 0001 1000 1001 0011 0111.
20 ops including two multiplies, or 29 elementary ops. 0 <= r <= 6292.
Not in book. */

unsigned divu1000a(unsigned n) {
unsigned q, r, t;

t = n - (n >> 6) - (n >> 3);
q = (n >> 10) + (n >> 16) + (n >> 17) + (n >> 21) + (n >> 24) +
(t >> 26);
r = n - q*1000;            // 0 <= r <= 6292.
return q + (4195*r >> 22); // Returning q + r/1000.
// {int t = r + (r << 1);     // Alternative showing
// t = t + (t << 5);          // expansion of multipli-
// t = t + (r << 12);         // cation by 4195.
// return q + (t >> 22);}
}

/* Code below uses 512/1000 = 0.1000 0011 0001 0010 0110 1110 1001 0111.
19 ops including one multiply, or 23 elementary ops. 0 <= r <= 1181. */

unsigned divu1000b(unsigned n) {
unsigned q, r, t;

t = (n >> 7) + (n >> 8) + (n >> 12);
q = (n >> 1) + t + (n >> 15) + (t >> 11) + (t >> 14);
q = q >> 9;
r = n - q*1000;
return q + ((r + 24) >> 10);
// return q + (r > 999);
}```

Last Edited: Sun. Nov 21, 2021 - 07:27 AM
```#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//modifyed Microchip AN526 alg.

/*
H       ;   T     ;  O          ;   B
-        ;   -      ;  -            ;   1010 0010  ;  162
-        ;   -      ;   xxx1    ;   010   0010  ;  <<#1
-        ;   -      ;   xx10     ;   10   0010    ;  <<#2
-        ;   -      ;   x101    ;  0   0010       ;  <<#3
-        ;   -      ;   1000    ;                      ; add 3
-        ;  xxx1 ;   0000    ;   0010           ;  <<#4
-        ;  xx10   ;   0000    ;   010             ;<<#5
-        ;   x100   ;   0000    ;   10               ;<<#6
-        ;   x1000   ;  0001    ;   0                ;<<#7
-        ;   1011   ;               ;                      ; add 3
1       ;    0110   ;   0010  ;                       ;<<#8
1      ;     6         ;   2        ;
*/

{
uint8_t tmp=r+0x03;
if(tmp&0x08) { r=tmp; }  // if (r+0x03)>7 (bit 3==1) r+=0x03
tmp=r+0x30;
if(tmp&0x80 ) {r =tmp ; } //bit7 , for packed if in the (r+0x03<<4 ) (bit 7==1) r+=0x03<<4 ;
return r;
}

uint8_t *  BinToBCD(uint16_t x  )
{

uint8_t count ;
uint8_t R;
R=0;
R=0;
//R=0;
//clear Carry and use shift ing of x or load MSB of x to Carry without shifting, then use shifting after

// Successive rotation
for (count=16 ; count>0 ; count--)  //total 16 steps
{
// add 3 for columns >=5
/*
if(Th>=5) { Th+=3; }
if(H>=5) { H+=3; }
if(T>=5) { T+=3; }
if(O>=5) { O+=3; }
*/
/*
*/

//R= (R<<1)|((R>>7)&0x01);   //rlf r2
R= (R<<1)|((R>>7)&0x01);  // rlf r1,  R1= (H<<1)| ((R0>>3)&0x01);
R= (R<<1)|((x>>count-1)&0x01); //  use carry for x shifting   into the
//x=x<<1;  (shift left once  to load data into carry )
/*
Th=0x0F&(Th<<1)| ((H>>3)&0x01);   //Th=0x0F&((Th<<1)|(H.bit3)
H=0x0F&(H<<1)| ((T>>3)&0x01);     //H=0x0F&((H<<1)|(T.bit3)
T=0x0F&(T<<1)| ((O>>3)&0x01);     //T=0x0F&((T<<1)|(O.bit3)
O=(uint8_t)0x0F&(O<<1)|((x>>15-i )&0x01);    //  MSB to O.bit0
*/
}

return R ;
}

int main()
{
uint16_t x=220;
for (x=0;x<=1023 ;x++)
{
uint8_t* R=BinToBCD(x );  //packed, use masks
printf ("\n x=%d ;   %d  %d % d %d   ",(int)x,(int)R>>4, (int)R&0x0f ,(int)R>>4 ,(int)R &0x0F  );
}

return 0 ;
}```

For uint16_t (you can modify for uint32_t,uint8_t ) binary to (packed) BCD (for LEDs )  you can use modifyed  Microchip AN526 subroutines .

Last Edited: Wed. Nov 24, 2021 - 07:43 AM