PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
reflow_oven.c
Go to the documentation of this file.
1 /*
2  * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
3  * All rights reserved.
4  * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
5  * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
6  * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation for any purpose, without fee, and without written agreement is
10  * hereby granted, provided that the above copyright notice, the following
11  * two paragraphs and the authors appear in all copies of this software.
12  *
13  * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
15  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
16  * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
23  *
24  * Please maintain this header in its entirety when copying/modifying
25  * these files.
26  *
27  *
28  */
29 #include "pic24_all.h"
30 #include <stdio.h>
31 #include "reflow_oven.h"
32 
33 /** \file
34  * Capstone- Reflow Oven control.
35  * Resources used:
36  * Timer2, Timer3, OC1, INT1, SPI
37 */
38 
39 
40 static inline void CONFIG_ZEROCROSS_ENABLE() {
41  CONFIG_RB6_AS_DIG_INPUT();
42  ENABLE_RB6_PULLUP();
43  CONFIG_INT1_TO_RP(6); //RP6 shares RB63 pin
44  DELAY_US(1);
45  _INT1IF = 0; //Clear the interrupt flag
46  _INT1IP = 1; //Choose a priority
47  _INT1EP = 0; //rising edge triggerred
48  _INT1IE = 1; //enable INT1 interrupt
49 }
50 
51 void configTimer2(void) {
52  T2CON = T2_OFF | T2_IDLE_CON | T2_GATE_OFF
53  | T2_32BIT_MODE_OFF
54  | T2_SOURCE_INT
55  | T2_PS_1_8 ; //1 tick = 0.2 us at FCY=40 MHz
56  PR2 = 0xFFFF;
57  TMR2 = 0; //clear timer2 value
58 }
59 
60 #define TRIAC_PW_HIGH 100 //in microseconds, triac gate pulse width high
61 #define HALF_CYCLE 8000 //in microseconds, part of half-cycle to use for triggering
62 #define CYCLE_OFFSET 200 //don't start right at zero crossing
63 uint8_t u8_currPowerSetting;
64 uint16_t u16_halfCycleTicks;
65 uint16_t u16_triacPWHighTicks;
66 uint16_t u16_cycleOffsetTicks;
67 
68 void configOutputCapture1(void) {
69  T2CONbits.TON = 0; //disable Timer when configuring Output compare
70  TMR2 = 0; //clear timer 2
71  CONFIG_OC1_TO_RP(14); //map OC1 to RP14/RB14
72  OC1R = 1; //want an immediate match so that OC1 is driven low
73  OC1RS = 2; //want an immediate match so that OC1 is driven low
74 //OC1 is initially off.
75  OC1CON = OC_TIMER2_SRC | //Timer2 source
76  OC_SINGLE_PULSE; //delayed one shot
77  T2CONbits.TON = 1; //enable the timer
78 }
79 
80 //AC power is controlled by triac by sending a delayed fixed-width pulse
81 //to the triac's gate every half-cycle. The delay from zero-crossing
82 //before the pulse is sent controls the power, the longer the delay,
83 //the less the power. The output capture OC_SINGLE pulse mode is used to
84 //generate the pulse.
85 static inline void TRIAC_ON() {
86  uint32_t u32_x;
87  T2CONbits.TON = 0; //disable Timer when configuring Output compare
88  TMR2 = 0; //clear timer 2
89  //later in the cycle is less power
90  u32_x = u16_halfCycleTicks;
91  u32_x = (u32_x * u8_currPowerSetting)/MAX_POWER_SETTING;
92  u32_x = u16_halfCycleTicks - u32_x;
93  OC1R = u32_x+u16_cycleOffsetTicks; //time for rising edge of pulse
94  OC1RS = u32_x+u16_triacPWHighTicks+u16_cycleOffsetTicks; //time for falling edge of pulse
95  OC1CON = OC_TIMER2_SRC | //Timer2 source
96  OC_SINGLE_PULSE; //delayed one shot
97  T2CONbits.TON = 1; //enable the timer
98 }
99 
100 //use output compare single shot mode, active high
101 void configureTriacControl() {
102  configTimer2(); //config Timer2
103  configOutputCapture1();
104  u16_halfCycleTicks = usToU16Ticks(HALF_CYCLE,getTimerPrescale(T2CONbits));
105  u16_triacPWHighTicks = usToU16Ticks(TRIAC_PW_HIGH, getTimerPrescale(T2CONbits));
106  u16_cycleOffsetTicks = usToU16Ticks(CYCLE_OFFSET, getTimerPrescale(T2CONbits));
107 }
108 
109 
110 //occurs at a 120 Hz rate, interrupted every time AC waveform crosses 0
111 void _ISRFAST _INT1Interrupt (void) {
112  _INT1IF = 0; //clear the interrupt bit
113  if (u8_currPowerSetting) TRIAC_ON(); //turn on triac
114 } //end _INT1Interrupt
115 
116 //use 16-bit mode
117 void configSPI1(void) {
118  //spi clock = 40MHz/1*64 = 40MHz/64 = 625 kHz
119  SPI1CON1 = SEC_PRESCAL_1_1 | //1:1 secondary prescale
120  PRI_PRESCAL_64_1 | //64:1 primary prescale
121  CLK_POL_ACTIVE_HIGH | //clock active high (CKP = 0)
122  SPI_CKE_OFF | //out changes inactive to active (CKE=0)
123  SPI_MODE16_ON | //16-bit mode
124  MASTER_ENABLE_ON; //master mode
125  //Only need SDI, SCLK since Thermocouple sensor is output only
126  CONFIG_SCK1OUT_TO_RP(7); //use RP7 for SCLK
127  CONFIG_SDI1_TO_RP(5); //use RP5 for SDI
128  CONFIG_SLAVE_ENABLE(); //chip select
129  SLAVE_DISABLE(); //disable the chip select
130  SPI1STATbits.SPIEN = 1; //enable SPI mode
131 }
132 
133 uint16_t readMAX6675(void) {
134  uint16_t u16_x;
135  SLAVE_ENABLE(); //assert chipselect
136  u16_x = ioMasterSPI1(0); //read theromouple
137  SLAVE_DISABLE();
138  return(u16_x);
139 }
140 
141 float getCelsiusFloatTemp(void) {
142  uint16_t u16_x;
143  float f_tempC;
144  u16_x = readMAX6675();
145  u16_x = u16_x >> 3; //drop status bits
146  f_tempC = u16_x; //convert to float
147  f_tempC = f_tempC/4; //10.2 format, divide by 4
148  return(f_tempC);
149 }
150 
151 //Round to nearest degree
152 int16_t getCelsiusI16Temp(void) {
153  uint16_t u16_x;
154  uint16_t u16_frac;
155 
156  u16_x = readMAX6675();
157  u16_frac = (u16_x & 0x1F) >> 3; //mask integer, drop status bits
158  u16_x = u16_x >> 5;
159  if (u16_frac >= 2) u16_x++; //round up
160  return((int16_t) u16_x);
161 }
162 
163 
164 
165 uint16_t volatile u16_tenthSeconds = 0;
166 
167 void _ISRFAST _T3Interrupt (void) {
168  _T3IF = 0; //clear the timer interrupt bit
169  u16_tenthSeconds++; //coarse timekeeper 0.1 second, all we need.
170 }
171 
172 #define ISR_PERIOD 100 // in ms
173 void configTimer3(void) {
174  //ensure that Timer2,3 configured as separate timers.
175  T2CONbits.T32 = 0; // 32-bit mode off
176  T3CON = T3_OFF |T3_IDLE_CON | T3_GATE_OFF
177  | T3_SOURCE_INT
178  | T3_PS_1_256 ;
179  PR3 = msToU16Ticks (ISR_PERIOD, getTimerPrescale(T3CONbits)) - 1;
180  TMR3 = 0; //clear timer3 value
181  _T3IF = 0; //clear interrupt flag
182  _T3IP = 1; //choose a priority
183  _T3IE = 1; //enable the interrupt
184  T3CONbits.TON = 1; //turn on the timer
185 }
186 
187 
188 void decrementPower(void) {
189  if (!u8_currPowerSetting) return;
190  u8_currPowerSetting--;
191 }
192 
193 void incrementPower(void) {
194  if (u8_currPowerSetting == MAX_POWER_SETTING) return;
195  u8_currPowerSetting++;
196 }
197 
198 void setPower(uint8_t newPower) {
199  u8_currPowerSetting = newPower;
200 }
201 
202 uint8_t getPower(void) {
203  return(u8_currPowerSetting);
204 }
205 
206 
207 void doMainMenu(void) {
208  uint8_t u8_c;
209  printf("Current profile: %s\n", getProfileDesc(u8_currentProfile));
210  printf("Reflow oven menu:\n");
211  printf(" 'S' - start oven for current profile \n");
212  printf(" 'p' - reflow profile menu \n");
213  printf(" 'd' - debug menu\n");
214  printf("Enter character: ");
215  u8_c = inCharEcho();
216  printf("\n");
217  if (u8_c == 'd') doDebugMenu();
218  if (u8_c == 'p') doProfileMenu();
219  if (u8_c == 'S') doReflow();
220  printf("\n");
221 }
222 
223 
224 int main (void) {
225  configBasic(HELLO_MSG);
226  configureTriacControl();
227  CONFIG_ZEROCROSS_ENABLE();
228  configSPI1();
229  configTimer3();
230  doRead(&fdata); //read temperature calibration data
231 
232  while (1) {
233  doMainMenu();
234  }
235 }