//takes a 16 bit value instead of an 8 bit value for maximum resolution void pwmWriteHR(uint8_t pin, uint16_t val) { pinMode(pin, OUTPUT); uint32_t tmp = val; if (val == 0) digitalWrite(pin, LOW); else if (val == 65535) digitalWrite(pin, HIGH); else { TimerData td = timer_to_pwm_data[digitalPinToTimer(pin)]; if(td.ChannelRegLoc) //null checking { if(td.Is16Bit) { sbi(_SFR_MEM8(td.PinConnectRegLoc), td.PinConnectBits); _SFR_MEM16(td.ChannelRegLoc) = (tmp * _SFR_MEM16(td.TimerTopRegLoc)) / 65535; } else { sbi(_SFR_MEM8(td.PinConnectRegLoc), td.PinConnectBits); _SFR_MEM8(td.ChannelRegLoc) = (tmp * _SFR_MEM8(td.TimerTopRegLoc)) / 65535; } } } }
static int pwm_set_duty_1_3(struct pwm *pwm, uint32_t val) { if (val > 0) val--; _SFR_MEM16(pwm->duty_reg_l) = val; return 0; }
void pwmWrite(uint8_t pin, uint8_t val) { pinMode(pin, OUTPUT); //casting "val" to be larger so that the final value (which is the partially //the result of multiplying two potentially high value int16s) will not truncate uint32_t tmp = val; if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH); else { uint16_t regLoc16 = 0; uint16_t regLoc8 = 0; uint16_t top; switch(digitalPinToTimer(pin)) { case TIMER0B: sbi(TCCR0A, COM0B1); regLoc8 = OCR0B_MEM; top = Timer0_GetTop(); break; case TIMER1A: sbi(TCCR1A, COM1A1); regLoc16 = OCR1A_MEM; top = Timer1_GetTop(); break; case TIMER1B: sbi(TCCR1A, COM1B1); regLoc16 = OCR1B_MEM; top = Timer1_GetTop(); break; case TIMER2B: sbi(TCCR2A, COM2B1); regLoc8 = OCR2B_MEM; top = Timer2_GetTop(); break; case NOT_ON_TIMER: default: if (val < 128) digitalWrite(pin, LOW); else digitalWrite(pin, HIGH); return; } if(regLoc16) _SFR_MEM16(regLoc16) = (tmp*top)/255; else _SFR_MEM8(regLoc8) = (tmp*top)/255; } }
// Set up PWM on the given pin boolean pwmInitDeciHertz(const IOPin* pin, uint32_t deciHertz, PERCENTAGE duty, uint32_t* actualDeciHertz){ boolean rtn = FALSE; const TimerCompare* channel = compareFromIOPin(pin); if(channel==null){ setError(PWM_PIN_NOT_AVAILABLE); }else{ // The pin is valid // The pin is valid and available TIMER_MODE mode; uint16_t icr; uint16_t prescaler; uint32_t actual; const Timer* timer = compareGetTimer(channel); // Find the best PWM setting boolean valid = timerCalcPwm(timer, deciHertz, 100, &mode, &icr, &prescaler, &actual); if(!valid){ // There is no PWM setting that is valid setError( (timerIsInUse(timer)) ? PWM_TIMER_IN_USE : TIMER_HAS_NO_PWM ); }else{ // Lets set up the PWM timerSetMode(timer,mode); if(modeIsICR(mode)){ // Set the ICR PORT icrPort = pgm_read_word(&timer->pgm_icr); _SFR_MEM16(icrPort)=icr; } // Mark the channel as in use // compareAttach(channel,&nullTimerCompareCallback,0,null); // Turn the pin into an output, low pin_make_output(pin, FALSE); // Turn on the PWM pin output compareSetOutputMode(channel, CHANNEL_MODE_NON_INVERTING); // Turn on the timer timerSetPrescaler(timer,prescaler); // Set the initial duty cycle pwmSetDutyCycle(pin,duty); // Set the return value if(actualDeciHertz){ *actualDeciHertz = actual; } rtn = TRUE; } } return rtn; }
//takes a 16 bit value instead of an 8 bit value for maximum resolution void pwmWriteHR(uint8_t pin, uint16_t val) { pinMode(pin, OUTPUT); uint32_t tmp = val; if (val == 0) digitalWrite(pin, LOW); else if (val == 65535) digitalWrite(pin, HIGH); else { uint16_t regLoc16 = 0; uint16_t regLoc8 = 0; uint16_t top; switch(digitalPinToTimer(pin)) { case TIMER0B: sbi(TCCR0A, COM0B1); regLoc8 = OCR0B_MEM; top = Timer0_GetTop(); break; case TIMER1A: sbi(TCCR1A, COM1A1); regLoc16 = OCR1A_MEM; top = Timer1_GetTop(); break; case TIMER1B: sbi(TCCR1A, COM1B1); regLoc16 = OCR1B_MEM; top = Timer1_GetTop(); break; case TIMER2B: sbi(TCCR2A, COM2B1); regLoc8 = OCR2B_MEM; top = Timer2_GetTop(); break; case NOT_ON_TIMER: default: if (val < 128) digitalWrite(pin, LOW); else digitalWrite(pin, HIGH); return; } if(regLoc16) _SFR_MEM16(regLoc16) = (tmp*top)/65535; else _SFR_MEM8(regLoc8) = (tmp*top)/65535; } }
/** * We have just hit top and are starting to count down again. * The new PWM duty cycle has been clocked in for the next servo */ static void service(const Timer *timer, void* data){ SERVO_DRIVER* driver = data; uint8_t i; // Move to the next servo uint8_t index = (driver->specific.softwareMUX.currentServo + 1)& 7; driver->specific.softwareMUX.currentServo = index; // Set the multiplex pins so the pulse goes to the correct servo // This needs to be done quickly to stop the start of the pulse // going to the wrong sevo uint8_t bits = index; for (i = 0; i < NUM_MUX_PINS; i++){ const IOPin* pin = driver->specific.softwareMUX.muxPins[i]; if(bits & 1){ pin_high(pin); }else{ pin_low(pin); } bits >>= 1; } // Keep track of the lowest value of the timer counter // Setting a delay between this value and TOP will mean the mux bits dont get changed in time uint16_t thisDelay = timerGetCounter(timer); if(thisDelay < maxDelay){ maxDelay = thisDelay; } // Time critical part is over // Now calculate the pulse width for next servo uint16_t newPos=0; index = (index + 1) & 7; if(index < driver->num_servos){ SERVO* servo = (SERVO *)pgm_read_word(&driver->servos[index]); if(servo->actuator.connected){ newPos = servo->delay; // Limit to the maximum value uint16_t limit = maxDelay; if(newPos > limit){ newPos = limit; } } } // Set the threshold inline - to save more registers push/pops //compareSetThreshold(channel,newPos); PORT port = driver->specific.softwareMUX.pwmPort; _SFR_MEM16(port) = newPos; // set 16 bit word }
static boolean initPWM(const IOPin* pin, uint32_t deciHertz){ const TimerCompare* channel = compareFromIOPin(pin); if(channel==null){ setError(PWM_PIN_NOT_AVAILABLE); return FALSE; } if(compareIsInUse(channel)){ setError(PWM_PIN_IN_USE); return FALSE; } TIMER_MODE mode; uint16_t icr; uint16_t prescaler; const Timer* timer = compareGetTimer(channel); // Find the best PWM setting for 10kHz, with 128 steps boolean valid = timerCalcPwm(timer, deciHertz, 128, &mode, &icr, &prescaler); if(!valid){ // There is no PWM setting that is valid setError( (timerIsInUse(timer)) ? PWM_TIMER_IN_USE : TIMER_HAS_NO_PWM ); }else{ // Lets set up the PWM if(!timerIsInUse(timer)){ timerSetMode(timer,mode); if(modeIsICR(mode)){ // Set the ICR PORT icrPort = pgm_read_word(&timer->pgm_icr); _SFR_MEM16(icrPort)=icr; } } // Make it an output pin and set high for brake pin_make_output(pin,TRUE); // Use inverting PWM compareSetOutputMode(channel,CHANNEL_MODE_INVERTING); // Mark the channels as in use compareAttach(channel,&nullTimerCompareCallback,0,null); // Do this last as it then turns on the timer timerSetPrescaler(timer,prescaler); } return valid; }
float GetPinResolution(uint8_t pin) { TimerData td = timer_to_pwm_data[digitalPinToTimer(pin)]; double baseTenRes = 0; if(td.ChannelRegLoc) { //getting a base 10 resolution td.Is16Bit? (baseTenRes = _SFR_MEM16(td.TimerTopRegLoc)) : (baseTenRes = _SFR_MEM8(td.TimerTopRegLoc)); //change the base and return return toBaseTwo(baseTenRes); } else { return 0; } }
static uint32_t pwm_get_duty_1_3(struct pwm *pwm) { uint32_t ret = _SFR_MEM16(pwm->duty_reg_l); return ret + 1; }