void pwm_update(uint16_t position, int32_t pwm) // Update the PWM signal being sent to the motor. The PWM value should be // a signed integer in the range of -65535 to -1 for clockwise movement, // 1 to 65535 for counter-clockwise movement or zero to stop all movement. // This function provides a sanity check against the servo position and // will prevent the servo from being driven past a minimum and maximum // position. { uint16_t pwm_width; // Determine if PWM is disabled in the registers. if (!(registers_read_byte(REG_FLAGS_LO) & (1<<FLAGS_LO_PWM_ENABLED))) pwm = 0; // Determine direction of servo movement or stop. if (pwm < 0) { // Less than zero. Turn clockwise. // Get the PWM width from the PWM value. pwm_width = (uint16_t) -pwm; // Turn clockwise. #if SWAP_PWM_DIRECTION_ENABLED pwm_dir_a(pwm_width); #else pwm_dir_b(pwm_width); #endif } else if (pwm > 0) { // More than zero. Turn counter-clockwise. // Get the PWM width from the PWM value. pwm_width = (uint16_t) pwm; // Turn counter-clockwise. #if SWAP_PWM_DIRECTION_ENABLED pwm_dir_b(pwm_width); #else pwm_dir_a(pwm_width); #endif } else { // Stop all PWM activity to the motor. pwm_stop(); } }
void pwm_update(uint16_t position, int16_t pwm) // Update the PWM signal being sent to the motor. The PWM value should be // a signed integer in the range of -255 to -1 for clockwise movement, // 1 to 255 for counter-clockwise movement or zero to stop all movement. // This function provides a sanity check against the servo position and // will prevent the servo from being driven past a minimum and maximum // position. { uint8_t pwm_width; uint16_t min_position; uint16_t max_position; // Quick check to see if the frequency divider changed. If so we need to // configure a new top value for timer/counter1. This value should only // change infrequently so we aren't too elegant in how we handle updating // the value. However, we need to be careful that we don't configure the // top to a value lower than the counter and compare values. if (registers_read_word(REG_PWM_FREQ_DIVIDER_HI, REG_PWM_FREQ_DIVIDER_LO) != pwm_div) { // Hold EN_A (PD2) and EN_B (PD3) low. PORTD &= ~((1<<PD2) | (1<<PD3)); // Give the H-bridge time to respond to the above, failure to do so or to wait long // enough will result in brownouts as the power is "crowbarred" to varying extents. // The delay required is also dependant on factors which may affect the speed with // which the MOSFETs can respond, such as the impedance of the motor, the supply // voltage, etc. // // Experiments (with an "MG995") have shown that 5microseconds should be sufficient // for most purposes. // delay_loop(DELAYLOOP); // Disable OC1A and OC1B outputs. TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0)); TCCR1A &= ~((1<<COM1B1) | (1<<COM1B0)); // Make sure that PWM_A (PB1/OC1A) and PWM_B (PB2/OC1B) are held low. PORTB &= ~((1<<PB1) | (1<<PB2)); // Reset the A and B direction flags. pwm_a = 0; pwm_b = 0; // Update the pwm frequency divider value. pwm_div = registers_read_word(REG_PWM_FREQ_DIVIDER_HI, REG_PWM_FREQ_DIVIDER_LO); // Update the timer top value. ICR1 = PWM_TOP_VALUE(pwm_div); // Reset the counter and compare values to prevent problems with the new top value. TCNT1 = 0; OCR1A = 0; OCR1B = 0; } // Are we reversing the seek sense? if (registers_read_byte(REG_REVERSE_SEEK) != 0) { // Yes. Swap the minimum and maximum position. // Get the minimum and maximum seek position. min_position = registers_read_word(REG_MAX_SEEK_HI, REG_MAX_SEEK_LO); max_position = registers_read_word(REG_MIN_SEEK_HI, REG_MIN_SEEK_LO); // Make sure these values are sane 10-bit values. if (min_position > MAX_POSITION) min_position = MAX_POSITION; if (max_position > MAX_POSITION) max_position = MAX_POSITION; // Adjust the values because of the reverse sense. min_position = MAX_POSITION - min_position; max_position = MAX_POSITION - max_position; } else { // No. Use the minimum and maximum position as is. // Get the minimum and maximum seek position. min_position = registers_read_word(REG_MIN_SEEK_HI, REG_MIN_SEEK_LO); max_position = registers_read_word(REG_MAX_SEEK_HI, REG_MAX_SEEK_LO); // Make sure these values are sane 10-bit values. if (min_position > MAX_POSITION) min_position = MAX_POSITION; if (max_position > MAX_POSITION) max_position = MAX_POSITION; } // Disable clockwise movements when position is below the minimum position. if ((position < min_position) && (pwm < 0)) pwm = 0; // Disable counter-clockwise movements when position is above the maximum position. if ((position > max_position) && (pwm > 0)) pwm = 0; // Determine if PWM is disabled in the registers. if (!(registers_read_byte(REG_FLAGS_LO) & (1<<FLAGS_LO_PWM_ENABLED))) pwm = 0; // Determine direction of servo movement or stop. if (pwm < 0) { // Less than zero. Turn clockwise. // Get the PWM width from the PWM value. pwm_width = (uint8_t) -pwm; // Turn clockwise. #if SWAP_PWM_DIRECTION_ENABLED pwm_dir_b(pwm_width); #else pwm_dir_a(pwm_width); #endif } else if (pwm > 0) { // More than zero. Turn counter-clockwise. // Get the PWM width from the PWM value. pwm_width = (uint8_t) pwm; // Turn counter-clockwise. #if SWAP_PWM_DIRECTION_ENABLED pwm_dir_a(pwm_width); #else pwm_dir_b(pwm_width); #endif } else { // Stop all PWM activity to the motor. pwm_stop(); } }
void pwm_update(uint16_t position, int16_t pwm) // Update the PWM signal being sent to the motor. The PWM value should be // a signed integer in the range of -255 to -1 for clockwise movement, // 1 to 255 for counter-clockwise movement or zero to stop all movement. // This function provides a sanity check against the servo position and // will prevent the servo from being driven past a minimum and maximum // position. { uint8_t pwm_width; uint16_t min_position; uint16_t max_position; // Quick check to see if the frequency divider changed. If so we need to // configure a new top value for timer/counter1. This value should only // change infrequently so we aren't too elegant in how we handle updating // the value. However, we need to be careful that we don't configure the // top to a value lower than the counter and compare values. if (registers_read_word(REG_PWM_FREQ_DIVIDER_HI, REG_PWM_FREQ_DIVIDER_LO) != pwm_div) { // Disable OC1A and OC1B outputs. TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0)); TCCR1A &= ~((1<<COM1B1) | (1<<COM1B0)); // Clear PB1 and PB2. PORTB &= ~((1<<PB1) | (1<<PB2)); delay_loop(DELAYLOOP); // Reset the A and B direction flags. pwm_a = 0; pwm_b = 0; // Update the pwm frequency divider value. pwm_div = registers_read_word(REG_PWM_FREQ_DIVIDER_HI, REG_PWM_FREQ_DIVIDER_LO); // Update the timer top value. ICR1 = PWM_TOP_VALUE(pwm_div); // Reset the counter and compare values to prevent problems with the new top value. TCNT1 = 0; OCR1A = 0; OCR1B = 0; } // Are we reversing the seek sense? if (registers_read_byte(REG_REVERSE_SEEK) != 0) { // Yes. Swap the minimum and maximum position. // Get the minimum and maximum seek position. min_position = registers_read_word(REG_MAX_SEEK_HI, REG_MAX_SEEK_LO); max_position = registers_read_word(REG_MIN_SEEK_HI, REG_MIN_SEEK_LO); // Make sure these values are sane 10-bit values. if (min_position > 0x3ff) min_position = 0x3ff; if (max_position > 0x3ff) max_position = 0x3ff; // Adjust the values because of the reverse sense. min_position = 0x3ff - min_position; max_position = 0x3ff - max_position; } else { // No. Use the minimum and maximum position as is. // Get the minimum and maximum seek position. min_position = registers_read_word(REG_MIN_SEEK_HI, REG_MIN_SEEK_LO); max_position = registers_read_word(REG_MAX_SEEK_HI, REG_MAX_SEEK_LO); // Make sure these values are sane 10-bit values. if (min_position > 0x3ff) min_position = 0x3ff; if (max_position > 0x3ff) max_position = 0x3ff; } // Disable clockwise movements when position is below the minimum position. if ((position < min_position) && (pwm < 0)) pwm = 0; // Disable counter-clockwise movements when position is above the maximum position. if ((position > max_position) && (pwm > 0)) pwm = 0; // Determine if PWM is disabled in the registers. if (!(registers_read_byte(REG_FLAGS_LO) & (1<<FLAGS_LO_PWM_ENABLED))) pwm = 0; // Determine direction of servo movement or stop. if (pwm < 0) { // Less than zero. Turn clockwise. // Get the PWM width from the PWM value. pwm_width = (uint8_t) -pwm; // Turn clockwise. #if SWAP_PWM_DIRECTION_ENABLED pwm_dir_a(pwm_width); #else pwm_dir_b(pwm_width); #endif } else if (pwm > 0) { // More than zero. Turn counter-clockwise. // Get the PWM width from the PWM value. pwm_width = (uint8_t) pwm; // Turn counter-clockwise. #if SWAP_PWM_DIRECTION_ENABLED pwm_dir_b(pwm_width); #else pwm_dir_a(pwm_width); #endif } else { // Stop all PWM activity to the motor. pwm_stop(); } }