Advertisement
grantman321

dual pwm first try

Feb 13th, 2015
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.49 KB | None | 0 0
  1. /* STAR_off_time version 1.2
  2. *
  3. * Changelog
  4. *
  5. * 1.0 Initial version
  6. * 1.1 Bug fix
  7. * 1.2 Added support for dual PWM outputs and selection of PWM mode per output level
  8. * 1.3 Added ability to have turbo ramp down gradually instead of step down
  9. *
  10. */
  11.  
  12. /*
  13. * NANJG 105C Diagram
  14. * ---
  15. * -| |- VCC
  16. * Star 4 -| |- Voltage ADC
  17. * Star 3 -| |- PWM
  18. * GND -| |- Star 2
  19. * ---
  20. *
  21. * FUSES
  22. * I use these fuse settings
  23. * Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
  24. * High: 0xff
  25. *
  26. * For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
  27. *
  28. * STARS
  29. * Star 2 = Moon if connected and alternate PWM output not used
  30. * Star 3 = H-L if connected, L-H if not
  31. * Star 4 = Capacitor for off-time
  32. *
  33. * VOLTAGE
  34. * Resistor values for voltage divider (reference BLF-VLD README for more info)
  35. * Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
  36. *
  37. * VCC
  38. * |
  39. * Vd (~.25 v drop from protection diode)
  40. * |
  41. * 1912 (R1 19,100 ohms)
  42. * |
  43. * |---- PB2 from MCU
  44. * |
  45. * 4701 (R2 4,700 ohms)
  46. * |
  47. * GND
  48. *
  49. * ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
  50. * 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
  51. * 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
  52. *
  53. * Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
  54. * 130 and 120
  55. *
  56. * To find out what value to use, plug in the target voltage (V) to this equation
  57. * value = (V * 4700 * 255) / (23800 * 1.1)
  58. *
  59. */
  60. #define F_CPU 4800000UL
  61.  
  62. /*
  63. * =========================================================================
  64. * Settings to modify per driver
  65. */
  66.  
  67. #define VOLTAGE_MON // Comment out to disable
  68.  
  69. #define MEMORY // Comment out to disable
  70.  
  71. //#define TICKS_250MS // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
  72. // Affects turbo timeout/rampdown timing
  73.  
  74. #define MODE_MOON 3 // Can comment out to remove mode, but should be set through soldering stars
  75. #define MODE_LOW 14 // Can comment out to remove mode
  76. #define MODE_MED 39 // Can comment out to remove mode
  77. //#define MODE_HIGH 255 // Can comment out to remove mode
  78. #define MODE_TURBO 255 // Can comment out to remove mode
  79. #define MODE_TURBO_LOW 150 // Level turbo ramps down to if turbo enabled
  80. #define TURBO_TIMEOUT 240 // How many WTD ticks before before dropping down. If ticks set for 500 ms, then 240 x .5 = 120 seconds. Max value of 255 unless you change "ticks"
  81. // variable to uint8_t
  82. #define TURBO_RAMP_DOWN // By default we will start to gradually ramp down, once TURBO_TIMEOUT ticks are reached, 1 PWM_LVL each tick until reaching MODE_TURBO_LOW PWM_LVL
  83. // If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
  84.  
  85. #define FAST_PWM_START 8 // Above what output level should we switch from phase correct to fast-PWM?
  86. #define DUAL_PWM_START 8 // Above what output level should we switch from the alternate PWM output to both PWM outputs? Comment out to disable alternate PWM output
  87.  
  88. #define ADC_LOW 130 // When do we start ramping
  89. #define ADC_CRIT 120 // When do we shut the light off
  90.  
  91. #define CAP_THRESHOLD 130 // Value between 1 and 255 corresponding with cap voltage (0 - 1.1v) where we consider it a short press to move to the next mode
  92. // Not sure the lowest you can go before getting bad readings, but with a value of 70 and a 1uF cap, it seemed to switch sometimes
  93. // even when waiting 10 seconds between presses.
  94.  
  95. /*
  96. * =========================================================================
  97. */
  98.  
  99. //#include <avr/pgmspace.h>
  100. #include <avr/io.h>
  101. #include <util/delay.h>
  102. #include <avr/interrupt.h>
  103. #include <avr/wdt.h>
  104. #include <avr/eeprom.h>
  105. #include <avr/sleep.h>
  106. //#include <avr/power.h>
  107.  
  108. #define STAR2_PIN PB0
  109. #define STAR3_PIN PB4
  110. #define CAP_PIN PB3
  111. #define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4)
  112. #define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3
  113. #define PWM_PIN PB1
  114. #define VOLTAGE_PIN PB2
  115. #define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
  116. #define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
  117. #define ADC_PRSCL 0x06 // clk/64
  118.  
  119. #define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
  120. #define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0
  121.  
  122. /*
  123. * global variables
  124. */
  125.  
  126. // Mode storage
  127. uint8_t eepos = 0;
  128. uint8_t eep[32];
  129. uint8_t memory = 0;
  130.  
  131. // Modes (gets set when the light starts up based on stars)
  132. static uint8_t modes[10]; // Don't need 10, but keeping it high enough to handle all
  133. volatile uint8_t mode_idx = 0;
  134. int mode_dir = 0; // 1 or -1. Determined when checking stars. Do we increase or decrease the idx when moving up to a higher mode.
  135. uint8_t mode_cnt = 0;
  136.  
  137. uint8_t lowbatt_cnt = 0;
  138.  
  139. void store_mode_idx(uint8_t lvl) { //central method for writing (with wear leveling)
  140. uint8_t oldpos=eepos;
  141. eepos=(eepos+1)&31; //wear leveling, use next cell
  142. // Write the current mode
  143. EEARL=eepos; EEDR=lvl; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
  144. while(EECR & 2); //wait for completion
  145. // Erase the last mode
  146. EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
  147. }
  148. inline void read_mode_idx() {
  149. eeprom_read_block(&eep, 0, 32);
  150. while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
  151. if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
  152. else eepos=0;
  153. }
  154.  
  155. inline void next_mode() {
  156. if (mode_idx == 0 && mode_dir == -1) {
  157. // Wrap around
  158. mode_idx = mode_cnt - 1;
  159. } else {
  160. mode_idx += mode_dir;
  161. if (mode_idx > (mode_cnt - 1)) {
  162. // Wrap around
  163. mode_idx = 0;
  164. }
  165. }
  166. }
  167.  
  168. inline void check_stars() {
  169. // Load up the modes based on stars
  170. // Always load up the modes array in order of lowest to highest mode
  171. // 0 being low for soldered, 1 for pulled-up for not soldered
  172. // Moon
  173. #ifdef MODE_MOON
  174. #ifndef DUAL_PWM_START
  175. if ((PINB & (1 << STAR2_PIN)) == 0) {
  176. #endif
  177. modes[mode_cnt++] = MODE_MOON;
  178. #ifndef DUAL_PWM_START
  179. }
  180. #endif
  181. #endif
  182. #ifdef MODE_LOW
  183. modes[mode_cnt++] = MODE_LOW;
  184. #endif
  185. #ifdef MODE_MED
  186. modes[mode_cnt++] = MODE_MED;
  187. #endif
  188. #ifdef MODE_HIGH
  189. modes[mode_cnt++] = MODE_HIGH;
  190. #endif
  191. #ifdef MODE_TURBO
  192. modes[mode_cnt++] = MODE_TURBO;
  193. #endif
  194. if ((PINB & (1 << STAR3_PIN)) == 0) {
  195. // High to Low
  196. mode_dir = -1;
  197. } else {
  198. mode_dir = 1;
  199. }
  200. }
  201.  
  202. inline void WDT_on() {
  203. // Setup watchdog timer to only interrupt, not reset
  204. cli(); // Disable interrupts
  205. wdt_reset(); // Reset the WDT
  206. WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
  207. #ifdef TICKS_250MS
  208. WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
  209. #else
  210. WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
  211. #endif
  212. sei(); // Enable interrupts
  213. }
  214.  
  215. inline void WDT_off()
  216. {
  217. cli(); // Disable interrupts
  218. wdt_reset(); // Reset the WDT
  219. MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
  220. WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
  221. WDTCR = 0x00; // Disable WDT
  222. sei(); // Enable interrupts
  223. }
  224.  
  225. inline void ADC_on() {
  226. DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
  227. ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
  228. ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
  229. }
  230.  
  231. inline void ADC_off() {
  232. ADCSRA &= ~(1<<7); //ADC off
  233. }
  234.  
  235. void set_output(uint8_t pwm_lvl) {
  236. #ifdef DUAL_PWM_START
  237. if (pwm_lvl > DUAL_PWM_START) {
  238. // Using the normal output along with the alternate
  239. PWM_LVL = pwm_lvl;
  240. } else {
  241. PWM_LVL = 0;
  242. }
  243. #else
  244. PWM_LVL = pwm_lvl;
  245. #endif
  246. // Always set alternate PWM value even if not compiled for dual output as we will use this value
  247. // throughout the code when trying to see what the current output level is. Setting this wont affect
  248. // the output when alternate output is disabled.
  249. ALT_PWM_LVL = pwm_lvl;
  250. }
  251.  
  252. #ifdef VOLTAGE_MON
  253. uint8_t low_voltage(uint8_t voltage_val) {
  254. // Start conversion
  255. ADCSRA |= (1 << ADSC);
  256. // Wait for completion
  257. while (ADCSRA & (1 << ADSC));
  258. // See if voltage is lower than what we were looking for
  259. if (ADCH < voltage_val) {
  260. // See if it's been low for a while
  261. if (++lowbatt_cnt > 8) {
  262. lowbatt_cnt = 0;
  263. return 1;
  264. }
  265. } else {
  266. lowbatt_cnt = 0;
  267. }
  268. return 0;
  269. }
  270. #endif
  271.  
  272. ISR(WDT_vect) {
  273. static uint8_t ticks = 0;
  274. if (ticks < 255) ticks++;
  275. // If you want more than 255 for longer turbo timeouts
  276. //static uint16_t ticks = 0;
  277. //if (ticks < 60000) ticks++;
  278.  
  279. #ifdef MODE_TURBO
  280. //if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
  281. if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
  282. #ifdef TURBO_RAMP_DOWN
  283. set_output(PWM_LVL - 1);
  284. #else
  285. // Turbo mode is always at end
  286. set_output(MODE_TURBO_LOW);
  287. if (MODE_TURBO_LOW <= modes[mode_idx-1]) {
  288. // Dropped at or below the previous mode, so set it to the stored mode
  289. // Kept this as it was the same functionality as before. For the TURBO_RAMP_DOWN feature
  290. // it doesn't do this logic because I don't know what makes the most sense
  291. store_mode_idx(--mode_idx);
  292. }
  293. #endif
  294. }
  295. #endif
  296.  
  297. }
  298.  
  299. int main(void)
  300. {
  301. // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
  302. #ifdef DUAL_PWM_START
  303. PORTB = (1 << STAR3_PIN);
  304. #else
  305. PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN);
  306. #endif
  307.  
  308. // Determine what mode we should fire up
  309. // Read the last mode that was saved
  310. read_mode_idx();
  311.  
  312. check_stars(); // Moving down here as it might take a bit for the pull-up to turn on?
  313.  
  314. // Start up ADC for capacitor pin
  315. DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption
  316. ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
  317. ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
  318.  
  319. // Wait for completion
  320. while (ADCSRA & (1 << ADSC));
  321. // Start again as datasheet says first result is unreliable
  322. ADCSRA |= (1 << ADSC);
  323. // Wait for completion
  324. while (ADCSRA & (1 << ADSC));
  325. if (ADCH > CAP_THRESHOLD) {
  326. // Indicates they did a short press, go to the next mode
  327. next_mode(); // Will handle wrap arounds
  328. store_mode_idx(mode_idx);
  329. } else {
  330. // Didn't have a short press, keep the same mode
  331. #ifndef MEMORY
  332. // Reset to the first mode
  333. mode_idx = ((mode_dir == 1) ? 0 : (mode_cnt - 1));
  334. store_mode_idx(mode_idx);
  335. #endif
  336. }
  337. // Turn off ADC
  338. ADC_off();
  339.  
  340. // Charge up the capacitor by setting CAP_PIN to output
  341. DDRB |= (1 << CAP_PIN); // Output
  342. PORTB |= (1 << CAP_PIN); // High
  343.  
  344. // Set PWM pin to output
  345. DDRB |= (1 << PWM_PIN);
  346. #ifdef DUAL_PWM_START
  347. DDRB |= (1 << STAR2_PIN);
  348. #endif
  349.  
  350. // Set timer to do PWM for correct output pin and set prescaler timing
  351. TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
  352. TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  353.  
  354. // Turn features on or off as needed
  355. #ifdef VOLTAGE_MON
  356. ADC_on();
  357. #else
  358. ADC_off();
  359. #endif
  360. ACSR |= (1<<7); //AC off
  361.  
  362. // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
  363. // Will allow us to go idle between WDT interrupts
  364. set_sleep_mode(SLEEP_MODE_IDLE);
  365.  
  366. uint8_t prev_mode_idx = mode_idx;
  367.  
  368. WDT_on();
  369.  
  370. // Now just fire up the mode
  371. // Set timer to do PWM for correct output pin and set prescaler timing
  372. if (modes[mode_idx] > FAST_PWM_START) {
  373. #ifdef DUAL_PWM_START
  374. TCCR0A = 0b10100011; // fast-PWM both outputs
  375. #else
  376. TCCR0A = 0b00100011; // fast-PWM normal output
  377. #endif
  378. } else {
  379. #ifdef DUAL_PWM_START
  380. TCCR0A = 0b10100001; // phase corrected PWM both outputs
  381. #else
  382. TCCR0A = 0b00100001; // phase corrected PWM normal output
  383. #endif
  384. }
  385. TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  386.  
  387. set_output(modes[mode_idx]);
  388.  
  389. uint8_t i = 0;
  390. uint8_t hold_pwm;
  391. while(1) {
  392. #ifdef VOLTAGE_MON
  393. if (low_voltage(ADC_LOW)) {
  394. // We need to go to a lower level
  395. if (mode_idx == 0 && ALT_PWM_LVL <= modes[mode_idx]) {
  396. // Can't go any lower than the lowest mode
  397. // Wait until we hit the critical level before flashing 10 times and turning off
  398. while (!low_voltage(ADC_CRIT));
  399. i = 0;
  400. while (i++<10) {
  401. set_output(0);
  402. _delay_ms(250);
  403. set_output(modes[0]);
  404. _delay_ms(500);
  405. }
  406. // Turn off the light
  407. set_output(0);
  408. // Disable WDT so it doesn't wake us up
  409. WDT_off();
  410. // Power down as many components as possible
  411. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  412. sleep_mode();
  413. } else {
  414. // Flash 3 times before lowering
  415. hold_pwm = ALT_PWM_LVL;
  416. i = 0;
  417. while (i++<3) {
  418. set_output(0);
  419. _delay_ms(250);
  420. set_output(hold_pwm);
  421. _delay_ms(500);
  422. }
  423. // Lower the mode by half, but don't go below lowest level
  424. if ((ALT_PWM_LVL >> 1) < modes[0]) {
  425. set_output(modes[0]);
  426. mode_idx = 0;
  427. } else {
  428. set_output(ALT_PWM_LVL >> 1);
  429. }
  430. // See if we should change the current mode level if we've gone under the current mode.
  431. if (ALT_PWM_LVL < modes[mode_idx]) {
  432. // Lower our recorded mode
  433. mode_idx--;
  434. }
  435. }
  436. // Wait 3 seconds before lowering the level again
  437. _delay_ms(3000);
  438. }
  439. #endif
  440. sleep_mode();
  441. }
  442.  
  443. return 0; // Standard Return Code
  444. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement