Guest User

Untitled

a guest
Feb 17th, 2019
214
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.15 KB | None | 0 0
  1. #include "ATmega8.h"
  2. #include <avr/interrupt.h>
  3.  
  4. // Various ESC transistor mappings are taken from SimonK firmware https://github.com/sim-/tgy
  5.  
  6. #if 0 // DYS 40A speed controller, from dys_nfet.inc
  7. // Output wires on DYS 40A, viewed from transistor side.
  8. // When ApFET_PORT BIT(ApFET) is CLEAR, output A is positive. When AnFET_PORT BIT(AnFET) is SET, output A is ground. Else disconnected.
  9. //  ______
  10. // |B C A||
  11. // |[] []||
  12. // |[] []||
  13. #define F_CPU 16000000
  14. #define USE_ICP 1
  15. #define rcp_in 0 // RC pulse input
  16. #define rcp_in_port REG_PINB
  17. #define ApFET 4
  18. #define AnFET 5
  19. #define BpFET 3
  20. #define BnFET 7
  21. #define CpFET 2
  22. #define CnFET 1
  23. #define ApFET_port REG_PORTD
  24. #define AnFET_port REG_PORTD
  25. #define BpFET_port REG_PORTD
  26. #define BnFET_port REG_PORTD
  27. #define CpFET_port REG_PORTD
  28. #define CnFET_port REG_PORTB
  29.  
  30. #define INIT_PB (BIT(HallA) | BIT(HallB) | BIT(HallC))
  31. #define DIR_PB  BIT(CnFET)
  32. #define INIT_PC (BIT(i2c_clk) | BIT(i2c_data))
  33. #define DIR_PC  0
  34. #define INIT_PD (BIT(ApFET) | BIT(BpFET) | BIT(CpFET) | BIT(txd))
  35. #define DIR_PD  (BIT(ApFET) | BIT(AnFET) | BIT(BpFET) | BIT(BnFET) | BIT(CpFET) | BIT(txd))
  36.  
  37. #define ApFET_off ApFET_port |=  BIT(ApFET)
  38. #define ApFET_on  ApFET_port &= ~BIT(ApFET)
  39. #define BpFET_off BpFET_port |=  BIT(BpFET)
  40. #define BpFET_on  BpFET_port &= ~BIT(BpFET)
  41. #define CpFET_off CpFET_port |=  BIT(CpFET)
  42. #define CpFET_on  CpFET_port &= ~BIT(CpFET)
  43.  
  44. #define AnFET_off AnFET_port &= ~BIT(AnFET)
  45. #define AnFET_on  AnFET_port |=  BIT(AnFET)
  46. #define BnFET_off BnFET_port &= ~BIT(BnFET)
  47. #define BnFET_on  BnFET_port |=  BIT(BnFET)
  48. #define CnFET_off CnFET_port &= ~BIT(CnFET)
  49. #define CnFET_on  CnFET_port |=  BIT(CnFET)
  50.  
  51. #elif 1 // HobbyKing 10A speed controller, from bs.inc
  52. // Output wires on HobbyKing 10A, viewed from transistor side.
  53. // When ApFET_PORT BIT(ApFET) is SET, output A is positive. When AnFET_PORT BIT(AnFET) is SET, output A is ground. Else disconnected.
  54. //  _____
  55. // |C B A|
  56. // |[] []|
  57. // |[] []|
  58. #define F_CPU 16000000
  59. #define USE_INT0 1
  60. #define rcp_in 2 // RC pulse input
  61. #define rcp_in_port REG_PIND
  62. #define ApFET 4
  63. #define AnFET 5
  64. #define BpFET 5
  65. #define BnFET 4
  66. #define CpFET 3
  67. #define CnFET 0
  68. #define ApFET_port REG_PORTD
  69. #define AnFET_port REG_PORTD
  70. #define BpFET_port REG_PORTC
  71. #define BnFET_port REG_PORTC
  72. #define CpFET_port REG_PORTC
  73. #define CnFET_port REG_PORTB
  74.  
  75. #define INIT_PB (BIT(HallA) | BIT(HallB) | BIT(HallC))
  76. #define DIR_PB  BIT(CnFET)
  77. #define INIT_PC 0
  78. #define DIR_PC  (BIT(BpFET) | BIT(BnFET) | BIT(CpFET))
  79. #define INIT_PD 0
  80. #define DIR_PD  (BIT(ApFET) | BIT(AnFET))
  81.  
  82. #define ApFET_off ApFET_port &= ~BIT(ApFET)
  83. #define ApFET_on  ApFET_port |=  BIT(ApFET)
  84. #define BpFET_off BpFET_port &= ~BIT(BpFET)
  85. #define BpFET_on  BpFET_port |=  BIT(BpFET)
  86. #define CpFET_off CpFET_port &= ~BIT(CpFET)
  87. #define CpFET_on  CpFET_port |=  BIT(CpFET)
  88.  
  89. #define AnFET_off AnFET_port &= ~BIT(AnFET)
  90. #define AnFET_on  AnFET_port |=  BIT(AnFET)
  91. #define BnFET_off BnFET_port &= ~BIT(BnFET)
  92. #define BnFET_on  BnFET_port |=  BIT(BnFET)
  93. #define CnFET_off CnFET_port &= ~BIT(CnFET)
  94. #define CnFET_on  CnFET_port |=  BIT(CnFET)
  95.  
  96. #else
  97. #error "No target speed controller specified"
  98. #endif
  99.  
  100. // PORTB pins that the hall sensors are connected to
  101. #define HallA 3
  102. #define HallB 4
  103. #define HallC 5
  104.  
  105. // Values for pwmState
  106. #define PWM_FREEWHEEL 0
  107. #define PWM_AnBp 1
  108. #define PWM_AnCp 2
  109. #define PWM_BnCp 3
  110. #define PWM_BnAp 4
  111. #define PWM_CnAp 5
  112. #define PWM_CnBp 6
  113. #define PWM_BRAKE 8
  114. #define PWM_OFF_DUTY BIT07 // Toggle on and off for active and inactive part of duty cycle
  115.  
  116. // These would need to be calibrated if using a normal RC transmitter, but bird wing sensor gloves always send this exact range.
  117. #define RC_PULSE_MIN 1024
  118. #define RC_PULSE_MAX 2048
  119.  
  120. // Special signal to save current position before turning off
  121. #define RC_PULSE_SAVE_MIN 450
  122. #define RC_PULSE_SAVE_MAX 550
  123.  
  124. // EEPROM addresses for variables to be saved
  125. #define EEPROM_CUR_POS 2
  126. #define EEPROM_MAX_POS 4
  127.  
  128. #define MAX_POWER 64
  129. #define POWER_CHANGE_RATE 1 // To avoid jolting from sudden power change, inc/dec toward target level by this amount each frame
  130. #define SOFT_STOP_DISTANCE 100 // Slow down when less than this many commutations from target position
  131. #define DEADBAND 4 // Don't bother moving if already this close to targetPos
  132.  
  133. #define SPEED_RANGE_NUM 4
  134. // 0: Very low speed, starting and stopping
  135. // 1: More than 2ms per commutation
  136. // 2: Between 1ms and 2ms per commutation
  137. // 3: Less than 1ms per commutation
  138.  
  139. static const u8 maxPower[SPEED_RANGE_NUM] = { 32, 40, 50, 64 }; // Limiting PWM duty based on motor speed is a rough method of current limiting.
  140.  
  141. // As the motor rotates forward, hall state cycles through 6 values (001b,011b,010b,110b,100b,101b) or in decimal (1,3,2,6,4,5).
  142. // Turning backward will step through that sequence in the opposite direction.
  143. // The next and prev state tables simply map from the current state to the next or previous in that sequence.
  144. static const u8 nextHallState[] = { 0, 5, 3, 1, 6, 4, 2, 0 };
  145. static const u8 prevHallState[] = { 0, 3, 6, 2, 5, 1, 4, 0 };
  146.  
  147. // This table says which hall state corresponds to which PWM state (e.g. if you want the motor to rotate to hall state 3,
  148. // then set PWM state to hallStateToPWMState[3]). The values are not universal. If changing to a different motor, or changing
  149. // the sensor layout, this table must be changed. The function DetectHallPhase will automatically detect the correct values,
  150. // although it still requires some manual copy/paste. Probably would be better to load it from EEPROM rather than hardcoding it here.
  151. static const u8 hallStateToPWMState[] = { 0, 6, 2, 1, 4, 5, 3, 0 };
  152.  
  153. // By the user physically moving the servo output shaft to the desired extremes while in calibration mode, the total number of motor
  154. // commutations between the two positions can be counted, and used to track the current position without a feedback potentiometer.
  155. // However, this method does require re-calibrating every time the power is turned off, or saving the current position to EEPROM
  156. // and assuming the servo shaft hasn't been moved while the power was off.
  157. s16 curPos = 0; // In commutations, range 0 to maxPos
  158. s8 curDir = 0; // Direction the motor is currently spinning, +1 or -1, or 0 if not moving.
  159. s16 maxPos = 100; // In commutations. Can be negative. Initial value here is fairly irrelevant since it's normally calibrated and then loaded from EEPROM.
  160. s16 targetPos = 0; // In commutations, calculated from the radio control PPM input value.
  161. u8 speedRange = 0; // Based on time between last two sensor state changes
  162. u8 hallState = 0; // Bit0 = hall sensor A, bit1 = hall sensor B, bit2 = hall sensor C.
  163. volatile u16 rcPulseTime = 0xffff; // In milliseconds. Should always be <= RC_PULSE_MAX except at startup.
  164. u8 pwmState = PWM_FREEWHEEL;
  165. u8 pwmDelay = MAX_POWER;
  166. volatile u8 hallTime = 0; // Number of timer2 overflows since last hall sensor state change
  167.  
  168. void EEPROMRead(u8 addr, u8 *dest, u8 size);
  169. void EEPROMWrite(u8 addr, const u8 *data, u8 size);
  170. void Update();
  171.  
  172. #if USE_ICP
  173. #define RC_PULSE_IS_RISING_EDGE        REG_TCCR1B & TCCR1B_ICP_RISING_EDGE
  174. #define RC_PULSE_WAIT_FOR_RISING_EDGE  REG_TCCR1B |= TCCR1B_ICP_RISING_EDGE
  175. #define RC_PULSE_WAIT_FOR_FALLING_EDGE REG_TCCR1B &= ~TCCR1B_ICP_RISING_EDGE
  176. #define RC_PULSE_ACKNOWLEDGE_INTERRUPT REG_TIFR = TIMSK_INPUT_CAPTURE
  177. ISR(TIMER1_CAPT_vect) // This is the function name to go with the brackets below the #endif
  178.  
  179. #elif USE_INT0
  180. #define RC_PULSE_IS_RISING_EDGE        REG_MCUCR == MCUCR_INT0_RISING_EDGE
  181. #define RC_PULSE_WAIT_FOR_RISING_EDGE  REG_MCUCR = MCUCR_INT0_RISING_EDGE
  182. #define RC_PULSE_WAIT_FOR_FALLING_EDGE REG_MCUCR = MCUCR_INT0_FALLING_EDGE
  183. #define RC_PULSE_ACKNOWLEDGE_INTERRUPT REG_GIFR = GICR_INT0
  184. ISR(INT0_vect) // This is the function name to go with the brackets below the #endif
  185.  
  186. #else
  187. #error "No RC pulse input method specified."
  188. #endif
  189. {
  190.     if (RC_PULSE_IS_RISING_EDGE)
  191.     {
  192.         RC_PULSE_WAIT_FOR_FALLING_EDGE;
  193.         REG_TCNT1 = 0; // Start counting the duration of the pulse
  194.         REG_TIFR = TIMSK_OC1B; // Clear overflow flag
  195.     }
  196.     else
  197.     {
  198.         #if (F_CPU != 16000000)
  199.         #error "Due to lazy programmer, conversion of timer ticks to RC pulse time only works if CPU freq is 16MHz"
  200.         #endif
  201.         rcPulseTime = REG_TCNT1 >> 1;
  202.         RC_PULSE_WAIT_FOR_RISING_EDGE;
  203.     }
  204.     RC_PULSE_ACKNOWLEDGE_INTERRUPT;
  205. }
  206.  
  207. ISR(TIMER0_OVF_vect) // Interrupt for PWM
  208. {
  209.     if (MAX_POWER - pwmDelay != 0) // Don't toggle if the resulting delay would be 0
  210.     {
  211.         pwmDelay = MAX_POWER - pwmDelay;
  212.         pwmState ^= PWM_OFF_DUTY;
  213.     }
  214.  
  215.     switch(pwmState)
  216.     {
  217.     case PWM_AnBp: BnFET_off, CnFET_off; ApFET_off, CpFET_off; AnFET_on, BpFET_on; break;
  218.     case PWM_AnCp: BnFET_off, CnFET_off; ApFET_off, BpFET_off; AnFET_on, CpFET_on; break;
  219.     case PWM_BnCp: AnFET_off, CnFET_off; ApFET_off, BpFET_off; BnFET_on, CpFET_on; break;
  220.     case PWM_BnAp: AnFET_off, CnFET_off; BpFET_off, CpFET_off; BnFET_on, ApFET_on; break;
  221.     case PWM_CnAp: AnFET_off, BnFET_off; BpFET_off, CpFET_off; CnFET_on, ApFET_on; break;
  222.     case PWM_CnBp: AnFET_off, BnFET_off; ApFET_off, CpFET_off; CnFET_on, BpFET_on; break;
  223.     case PWM_BRAKE: ApFET_off, BpFET_off, CpFET_off, AnFET_on, BnFET_on, CnFET_on; break;
  224.     case PWM_FREEWHEEL:
  225.     // If freewheeling, or if PWM_OFF_DUTY flag is set, turn all transistors off.
  226.     default: AnFET_off, BnFET_off; CnFET_off, ApFET_off, BpFET_off, CpFET_off; break;
  227.     }
  228.  
  229.     REG_TCNT0 = (u8)-pwmDelay;
  230.     REG_TIFR = TIMSK_TM0_OVERFLOW;
  231. }
  232.  
  233. ISR(TIMER2_OVF_vect) // Interrupt for measuring motor speed
  234. {
  235.     if (hallTime != 0xff)
  236.         hallTime++;
  237.     REG_TIFR = TIMSK_TM2_OVERFLOW;
  238. }
  239.  
  240. void EEPROMRead(u8 addr, u8 *dest, u8 size)
  241. {
  242.     REG_EEAR_H = 0;
  243.     while(size--)
  244.     {
  245.         REG_EEAR_L = addr++;
  246.         REG_EECR = EECR_READ;
  247.         *dest++ = REG_EEDR;
  248.     }
  249. }
  250.  
  251. // Note: Interrupts must be disabled before calling this function
  252. void EEPROMWrite(u8 addr, const u8 *data, u8 size)
  253. {
  254.     REG_EEAR_H = 0;
  255.     while(size--)
  256.     {
  257.         REG_EEAR_L = addr++;
  258.         REG_EEDR = *data++;
  259.         REG_EECR |= EECR_WRITE_ENABLE;
  260.         REG_EECR |= EECR_WRITE;
  261.         while(REG_EECR & EECR_WRITE) {}
  262.     }
  263. }
  264.  
  265. STIN void wait(volatile u8 ticks) { ticks>>=2; while(ticks--){} }
  266. STIN void waitLong(u32 ticks) { while(ticks > 255){wait(255); ticks-=255;} wait((u8)ticks); }
  267. void beep(u16 period)
  268. {
  269.     u8 cycles = 30;
  270.  
  271.     REG_SREG &= ~SREG_IRQ_ENABLE;
  272.     AnFET_off, BnFET_off; CnFET_off, ApFET_off, BpFET_off, CpFET_off;
  273.  
  274.     while(--cycles)
  275.     {
  276.         BnFET_on;
  277.         CpFET_on;
  278.         waitLong(1000);
  279.         CpFET_off;
  280.         ApFET_on;
  281.         waitLong(1000);
  282.         ApFET_off;
  283.         BnFET_off;
  284.         waitLong(period - 2000);
  285.     }
  286.  
  287.     REG_SREG |= SREG_IRQ_ENABLE;
  288. }
  289.  
  290. // Call this function to automatically detect which hall state corresponds to which PWM state.
  291. // For each PWM state, transistors are pulsed to pull the rotor into position, and then the hall state is read.
  292. // The results are written to EEPROM addresses 16-23. Dump EEPROM and copy/paste the values into hallStateToPWMState.
  293. //
  294. // The first and last entries of hallStateToPWMState are unused and should be 0 (because there should never be
  295. // a time when all hall sensors are turned off or all turned on).
  296. // The rest should have values 1 to 6 in some order, with no duplicates.
  297. // Valid output should look something like 0, 6, 2, 1, 4, 5, 3, 0
  298. // The numbers correspond to the PWM_AnBp, PWM_AnCp, etc. #defines
  299. //
  300. // Appropriate power level depends on the motor, battery, and any load on the output.
  301. // Too low and the rotor won't move, too high and transistors may overheat or cause power brownout.
  302. // Start low and if the table is incomplete, raise it and try again.
  303. void DetectHallPhase(u8 power)
  304. {
  305.     u8 i, j;
  306.     u8 table[8] = {0};
  307.  
  308.     for (i = 1; i <= 6; i++)
  309.     {
  310.         REG_SREG &= ~SREG_IRQ_ENABLE;
  311.         pwmState = i;
  312.         pwmDelay = power;
  313.         REG_SREG |= SREG_IRQ_ENABLE;
  314.         for (j = 0; j < 20; j++)
  315.             waitLong(60000);
  316.         table[(REG_PINB >> 3) & 7] = i;
  317.     }
  318.  
  319.     REG_SREG &= ~SREG_IRQ_ENABLE;
  320.     pwmState = PWM_FREEWHEEL;
  321.     pwmDelay = 0;
  322.     AnFET_off, BnFET_off; CnFET_off, ApFET_off, BpFET_off, CpFET_off;
  323.     REG_SREG |= SREG_IRQ_ENABLE;
  324.  
  325.     REG_SREG &= ~SREG_IRQ_ENABLE;
  326.     EEPROMWrite(16, table, 8);
  327.     REG_SREG |= SREG_IRQ_ENABLE;
  328. }
  329.  
  330. int main(void)
  331. {
  332.     REG_SREG = 0;
  333.     REG_DDRB = DIR_PB;
  334.     REG_PORTB = INIT_PB;
  335.     REG_DDRC = DIR_PC;
  336.     REG_PORTC = INIT_PC;
  337.     REG_DDRD = DIR_PD;
  338.     REG_PORTD = INIT_PD;
  339.  
  340.     // Timer for PWM. Interrupt in REG_TIMSK is enabled further below.
  341.     REG_TCCR0 = TCCR0_CLOCK_8;
  342.  
  343.     // Timer for measuring motor speed
  344.     REG_TCCR2 = TCCR2_CLOCK_8;
  345.  
  346.     // Timer and interrupt for radio control PPM input
  347.     REG_OCR1B = RC_PULSE_MAX << 1;
  348.     #if USE_ICP
  349.     REG_TCCR1B = TCCR0_CLOCK_8 | TCCR1B_ICP_RISING_EDGE | TCCR1B_ICP_NOISE_CANCEL;
  350.     REG_TIMSK = TIMSK_INPUT_CAPTURE | TIMSK_TM0_OVERFLOW | TIMSK_TM2_OVERFLOW;
  351.     #elif USE_INT0
  352.     REG_TCCR1B = TCCR0_CLOCK_8;
  353.     REG_TIMSK = TIMSK_TM0_OVERFLOW | TIMSK_TM2_OVERFLOW;
  354.     REG_MCUCR = MCUCR_INT0_RISING_EDGE;
  355.     REG_GICR = GICR_INT0;
  356.     #endif
  357.  
  358.     REG_SREG |= SREG_IRQ_ENABLE;
  359.  
  360.     while(rcPulseTime == 0xffff) {} // Wait for first RC pulse input
  361.  
  362.     //DetectHallPhase(48);
  363.  
  364.     beep(30000);
  365.  
  366.     hallState = (REG_PINB >> 3) & 7;
  367.  
  368.     // High input on startup triggers calibration mode
  369.     if (rcPulseTime > ((RC_PULSE_MIN + RC_PULSE_MAX) >> 1))
  370.     {
  371.         while(rcPulseTime > ((RC_PULSE_MIN + RC_PULSE_MAX) >> 1))
  372.         {
  373.             // Update curPos as the user physically rotates the servo arm to the desired max position
  374.             const u8 lastHallState = hallState;
  375.             hallState = (REG_PINB >> 3) & 7;
  376.             if (hallState == nextHallState[lastHallState])
  377.                 curPos++;
  378.             else if (hallState == prevHallState[lastHallState])
  379.                 curPos--;
  380.         }
  381.         maxPos = targetPos = curPos;
  382.         REG_SREG &= ~SREG_IRQ_ENABLE;
  383.         EEPROMWrite(EEPROM_CUR_POS, (const u8*)&curPos, sizeof(curPos));
  384.         EEPROMWrite(EEPROM_MAX_POS, (const u8*)&maxPos, sizeof(maxPos));
  385.         REG_SREG |= SREG_IRQ_ENABLE;
  386.     }
  387.     else
  388.     {
  389.         REG_SREG &= ~SREG_IRQ_ENABLE;
  390.         EEPROMRead(EEPROM_CUR_POS, (u8*)&curPos, sizeof(curPos));
  391.         EEPROMRead(EEPROM_MAX_POS, (u8*)&maxPos, sizeof(maxPos));
  392.         REG_SREG |= SREG_IRQ_ENABLE;
  393.         targetPos = curPos;
  394.     }
  395.  
  396.     while(1)
  397.         Update();
  398. }
  399.  
  400. void Update()
  401. {
  402.     // rcPulseTime and hallTime are copied because they could be changed by interrupts
  403.     const u16 lastRCPulseTime = rcPulseTime;
  404.     const u8 lastHallTime = hallTime;
  405.     const u8 lastHallState = hallState;
  406.  
  407.     hallState = (REG_PINB >> 3) & 7;
  408.  
  409.     // Update RC pulse input
  410.     if (lastRCPulseTime >= RC_PULSE_MIN)
  411.         targetPos = (s16)((s32)(lastRCPulseTime - RC_PULSE_MIN) * (s32)maxPos >> 10);
  412.     else if (lastRCPulseTime >= RC_PULSE_SAVE_MAX && lastRCPulseTime <= RC_PULSE_SAVE_MAX)
  413.     {
  414.         REG_SREG &= ~SREG_IRQ_ENABLE;
  415.         AnFET_off, BnFET_off; CnFET_off, ApFET_off, BpFET_off, CpFET_off; // Must not leave any FETs active during this time consuming operation
  416.         EEPROMWrite(EEPROM_CUR_POS, (const u8*)&curPos, sizeof(curPos));
  417.         REG_SREG |= SREG_IRQ_ENABLE;
  418.     }
  419.  
  420.     // Update hall sensor state
  421.     if (hallState != lastHallState)
  422.     {
  423.         if (curDir == 0 || ABS(curPos - targetPos) < SOFT_STOP_DISTANCE)
  424.             speedRange = 0;
  425.         else if (lastHallTime > (4096 >> 8)) speedRange = 1; // Greater than 2ms per commutation but not starting/stopping
  426.         else if (lastHallTime > (2048 >> 8)) speedRange = 2; // 1ms to 2ms per commutation (note: there are 2 timer ticks per microsecond)
  427.         else speedRange = 3; // Less than 1ms per commutation can go full power
  428.  
  429.         if (hallState == nextHallState[lastHallState])
  430.             curDir = +1;
  431.         else if (hallState == prevHallState[lastHallState])
  432.             curDir = -1;
  433.         else    // we missed a commutation and curPos is no longer accurate. Need a feedback potentiometer to fix this.
  434.             curDir = 0;
  435.  
  436.         curPos += curDir;
  437.         hallTime = 0; // Start counting time until next sensor state change
  438.     }
  439.     else if (hallTime == 0xff)
  440.     {
  441.         // Not moving
  442.         curDir = 0;
  443.         speedRange = 0;
  444.         hallTime = 0;
  445.     }
  446.  
  447.     // Update PWM state
  448.     if (ABS(curPos - targetPos) < DEADBAND || (curPos < targetPos && curDir < 0) || (curPos > targetPos && curDir > 0))
  449.     {
  450.         // Brake if at target position or moving in the wrong direction
  451.         speedRange = 0;
  452.         pwmState = PWM_BRAKE;
  453.         pwmDelay = MAX_POWER;
  454.     }
  455.     else
  456.     {
  457.         REG_SREG &= ~SREG_IRQ_ENABLE;
  458.         pwmState = (pwmState & PWM_OFF_DUTY) |
  459.             hallStateToPWMState[(curPos < targetPos) ? nextHallState[hallState] : prevHallState[hallState]];
  460.         pwmDelay = (pwmState & PWM_OFF_DUTY) ? (MAX_POWER - maxPower[speedRange]) : maxPower[speedRange];
  461.         REG_SREG |= SREG_IRQ_ENABLE;
  462.     }
  463. }
Add Comment
Please, Sign In to add comment