Lowering fixed PWM frequency for boost controller?

Working on a arduino uno boost controller using a oled display. Controller is functional but I have found out in bench testing that 30.64 Hz is still too fast of a frequency for my 4 port mac air valve. Controller consists of a OLED display, potentiometer, transistor and supporting circuitry (resistor and diode for the transistor/mac valve).

GOALS OF THIS POST:
I've have searched this forum for days, along with other places on the web. I've downloaded the ATmega datasheet https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf and have gone through the part about the timers (starts on page 113) and I cant totally get my head wrapped around how to implement it. I need to figure out a way to have the frequency of the timer1 fixed somewhere in the 15-17Hz range, doesn't need to be exact because its running a mechanical air solenoid, while still allowing the duty cycle of the timer1 be changed on the fly using my potentiometer input. I used the TimerPWMCheatsheet to set timer1 to the 30.64Hz and have the duty cycle adjustable via analog reading a potentiometer and mapping said value out to analogWrite on digitalPin 9. Heres the current working code in the controller:

edit: Removed code

Ok now I've tried the following code on my bench which I found the timer parts of the code on this forum, but it isnt working, which I believe is because I'm still trying to analog write the now D10 pin instead of how its actually supposed to be done with the timer1 setup in this fashion, which I dont know how to write/implement. The code compiles, but it has no transistor output at all as viewed from my oscilloscope.

edit: removed code

So as stated before, I really just need some guidance/help on how to setup the timer1 to have the frequency in the 15-17Hz range. I know in the last code I posted I was setting it up in the 10Hz range, but thats because I wasnt sure what number to set it at to get the Hz frequency I'm shooting for, plus I just wanted to see if it would work on my bench test unit, which it didnt.

If you need any more info please let me know, and thanks for any help in advance.

In the working sketch number one at 30 Hz, you are relying on the default timer setup, which is PWM to 255 where the timer counts up to 255 and back down to 0. The normal prescaler is 64 which gives the default Timer1 period of .062564511 which gives the default frequency of 490Hz. You have changed the prescaler to 1024 so the frequency is divided by 16 to get your 30Hz.

The most simple thing to cut the frequency in half and leave the code the same is to change the mode to PWM to 511. The period will be doubled and you'll wind up with 15Hz.

Try with this change. You still should be able to use analogWrite() on the output, and the range of the argument will double.

 noInterrupts(); // Disable interrupts so I can set the PWM frequency of the effing boost controller pin to 15Hz
TCCR1A = (TCCR1A & 0b11110000) | 0x02; //set mode 2  PWM to 511
TCCR1B = (TCCR1B & 0b11111000) | 0x05; // This line of code sets that frequency up. Borrowed from https://playground.arduino.cc/Main/TimerPWMCheatsheet
 interrupts(); // Reattach the interrupts and lets go to town.

cattledog Ok so that decreased my frequency, but it looks like anything at 50% DC it goes straight to 100% DC then drops back down and acts like it should. I attached my oscilloscope pictures of before and after that line of code you posted, showing the frequency did decrease, thank you!

transistorOntime = map(dutycycleReq, 0, 100, 0, 255);

When I switched this code to 510 or 511 at the end, which I thought would be the new scale because of doubling TCCR1A's range, I ended up having 100% duty cycle half way though the potentiometers sweep, then it would go back to acting the way it should upto 100% duty cycle. So for example, it would go from 49% DC to 100% DC to 51% duty cycle and kept climbing. I think I'm going to make a simple if statement making it jump the 50% duty cycle but I'd like to understand why its doing this.

May I also ask for my own understanding what the settings can be on TCCR1A? I understand its relationship between TCCR1A & TCCR1B but the PWM cheat sheet only gives the settings for TCCR1B unless I'm totally misunderstanding it, which is possible.

Thanks again for the help though!

After code help.jpg

Before code help.jpg

Ok, got it figured out as far as this application. So I found out via serial monitor whenever analogwrite() is set to 255, it must be commanding pin 9 100% DC, because after that it goes right back to the way it should be working its way up through the sweep. So my work around is a simple if statement. So if the mapped analogwrite() is set to 255, it gets changed to 256, which makes the duty cycle output sweep just like it should when checked with my oscilloscope.

dialaLoad2 = analogRead(dialaLoad); // read off the dial-a-load or boost knob
  float dutycycleReq = map(dialaLoad2, 8, 1022, 0, 100);
  Serial.print(dutycycleReq);
  Serial.print('\t');
  Serial.println(transistorOntime);
  transistorOntime = map(dutycycleReq, 0, 100, 0, 511);
  if(transistorOntime == 255){
    transistorOntime = 256;
  }
  analogWrite(transistorPin, transistorOntime);

Thanks again cattledog for helping me with this! I'd still love to understand TCCR1A's available settings in case I need them for future projects, along with anyone else who searches this forum. Thanks again!

Good job figuring out the detail of how to use analogWrite() with the changed timer range. Your solution is simple. +1

I hadn't thought through all the implications when I posted my simple fix which made minimal changes to your sketch and did not introduce any extra low level timer register code.

Here is the function for analogWrite() from the source code in wiring_analog.c. You can see that the function defaults to digitalWrite() for values of 0 and 255. Otherwise (when using a pin on Timer 1) it sets the timer1 compare value for the pwm duty cycle to OCR1A or OCR1B depending on the output pin. You may want to modify your fix to include setting the pin high with digitalWrite() when the input value is at the top end, as well as removing the 255 settting issue.

void analogWrite(uint8_t pin, int val)
{
	// We need to make sure the PWM output is enabled for those pins
	// that support it, as we turn it off when digitally reading or
	// writing with them.  Also, make sure the pin is in output mode
	// for consistenty with Wiring, which doesn't require a pinMode
	// call for the analog output pins.
	pinMode(pin, OUTPUT);
	if (val == 0)
	{
		digitalWrite(pin, LOW);
	}
	else if (val == 255)
	{
		digitalWrite(pin, HIGH);
	}
	else
	{
		switch(digitalPinToTimer(pin))
		{
			// XXX fix needed for atmega8
			#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
			case TIMER0A:
				// connect pwm to pin on timer 0
				sbi(TCCR0, COM00);
				OCR0 = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR0A) && defined(COM0A1)
			case TIMER0A:
				// connect pwm to pin on timer 0, channel A
				sbi(TCCR0A, COM0A1);
				OCR0A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR0A) && defined(COM0B1)
			case TIMER0B:
				// connect pwm to pin on timer 0, channel B
				sbi(TCCR0A, COM0B1);
				OCR0B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR1A) && defined(COM1A1)
			case TIMER1A:
				// connect pwm to pin on timer 1, channel A
				sbi(TCCR1A, COM1A1);
				OCR1A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR1A) && defined(COM1B1)
			case TIMER1B:
				// connect pwm to pin on timer 1, channel B
				sbi(TCCR1A, COM1B1);
				OCR1B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR1A) && defined(COM1C1)
			case TIMER1C:
				// connect pwm to pin on timer 1, channel B
				sbi(TCCR1A, COM1C1);
				OCR1C = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR2) && defined(COM21)
			case TIMER2:
				// connect pwm to pin on timer 2
				sbi(TCCR2, COM21);
				OCR2 = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR2A) && defined(COM2A1)
			case TIMER2A:
				// connect pwm to pin on timer 2, channel A
				sbi(TCCR2A, COM2A1);
				OCR2A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR2A) && defined(COM2B1)
			case TIMER2B:
				// connect pwm to pin on timer 2, channel B
				sbi(TCCR2A, COM2B1);
				OCR2B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR3A) && defined(COM3A1)
			case TIMER3A:
				// connect pwm to pin on timer 3, channel A
				sbi(TCCR3A, COM3A1);
				OCR3A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR3A) && defined(COM3B1)
			case TIMER3B:
				// connect pwm to pin on timer 3, channel B
				sbi(TCCR3A, COM3B1);
				OCR3B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR3A) && defined(COM3C1)
			case TIMER3C:
				// connect pwm to pin on timer 3, channel C
				sbi(TCCR3A, COM3C1);
				OCR3C = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR4A)
			case TIMER4A:
				//connect pwm to pin on timer 4, channel A
				sbi(TCCR4A, COM4A1);
				#if defined(COM4A0)		// only used on 32U4
				cbi(TCCR4A, COM4A0);
				#endif
				OCR4A = val;	// set pwm duty
				break;
			#endif
			
			#if defined(TCCR4A) && defined(COM4B1)
			case TIMER4B:
				// connect pwm to pin on timer 4, channel B
				sbi(TCCR4A, COM4B1);
				OCR4B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR4A) && defined(COM4C1)
			case TIMER4C:
				// connect pwm to pin on timer 4, channel C
				sbi(TCCR4A, COM4C1);
				OCR4C = val; // set pwm duty
				break;
			#endif
				
			#if defined(TCCR4C) && defined(COM4D1)
			case TIMER4D:				
				// connect pwm to pin on timer 4, channel D
				sbi(TCCR4C, COM4D1);
				#if defined(COM4D0)		// only used on 32U4
				cbi(TCCR4C, COM4D0);
				#endif
				OCR4D = val;	// set pwm duty
				break;
			#endif

							
			#if defined(TCCR5A) && defined(COM5A1)
			case TIMER5A:
				// connect pwm to pin on timer 5, channel A
				sbi(TCCR5A, COM5A1);
				OCR5A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR5A) && defined(COM5B1)
			case TIMER5B:
				// connect pwm to pin on timer 5, channel B
				sbi(TCCR5A, COM5B1);
				OCR5B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR5A) && defined(COM5C1)
			case TIMER5C:
				// connect pwm to pin on timer 5, channel C
				sbi(TCCR5A, COM5C1);
				OCR5C = val; // set pwm duty
				break;
			#endif

			case NOT_ON_TIMER:
			default:
				if (val < 128) {
					digitalWrite(pin, LOW);
				} else {
					digitalWrite(pin, HIGH);
				}
		}
	}
}