所有关于电路”class=
项目

回流焊炉的设计自己的控制器

2015年11月17日通过帕特里克·劳埃德

在上一期中,我们构建了硬件控制双向可控硅作为第一步的交流波形DIY回流焊炉。本教程继续该项目通过展示如何设置底层硬件测量温度,读取零交叉探测器,驱动双向可控硅,并打印使用USART串行终端。

继续从之前的教程,这个项目将向您展示如何设置低级硬件测量温度,读取零交叉探测器,驱动双向可控硅,并打印使用USART串行终端。

介绍

请参阅第1部分:单片机控制交流电源

上次,我们建立了双向可控硅驱动和零交叉检测电路和120 v交流电源电压接口。这是一个非常有能力的电路,但没有一个合适的控制器,最终的结果并不那么有趣,因为它只能打开或关闭波形和不暗。在这个项目中,我们正在编写的C代码爱特梅尔公司ATmega328P单片机完成几个关键任务:1。读零交叉与外部中断信号,驱动双向可控硅脉冲宽度调制2的特殊形式。使用通用的同步和异步串行接收器和发射器(USART)显示调试数据3。接口与MAX31855热电偶放大器串行外围接口(SPI) 4。创建一个通用毫秒计时器来帮助促进超时,时间戳,非阻塞延迟

裸金属C意味着我们正在编写非常低级代码——C是一个加强从汇编语言抽象。这意味着我们会操纵特定寄存器中的位,指定中断向量直接在我们的中断服务例程(isr),有时处理原始内存分配malloc ()。有一些宏,让我们在这个过程更容易一些macros.h(使代码更清晰阅读),但是熟悉的一些实际ATmega328P的内部运作,它使用不同的寄存器名称和组件是非常重要的。的完整的数据表(PDF)的芯片有所有信息和值得。从Gound编程也可能是一个有用的资源获得适应低层次开发。

我使用一些代码安迪•布朗和他的ATmega8烤箱控制器。有一些插入的代码重用,一些调整,一些完全不同的实现。除了拥有一个不同的控制器,他写了他在c++代码,并使用不同的构建系统,但我仍然想要给他完整的信用之前他所做的工作。

供应需要

这个项目主要是软件,所以数相对较小的部分。你需要:

  • 3.3 v ATmega328P单片机与晶体振荡器(属性USART所需功能)
  • 在线连续程序员(ICSP)
    • AVR的龙——我使用这个。很多特性和相对便宜
    • Arduino Uno——其他主要Arduino电路板也可以被用作一个程序员。
  • usb串口适配器
    • CH340 / CH341
    • FT232RL- - - - - -需要在3.3 v工作!我有这个5 v模型但我切痕迹背面和添加一个开关:

  • MAX31855突破
  • 功能双向可控硅交流控制器
  • 电脑运行Linuxavrdude,binutils-avr,gcc-avr,avr-libc,gdb-avr安装。可以在Windows或Mac但是这超出了这个项目的范围。

双向可控硅控制器

本节是控制器的面包和黄油。的oven_control.c文件由几个部分组成:一个oven_setup (),oven_setDutyCycle(百分比),三个isr处理不同时间关键的事件。

烤箱控制器Initization函数

无效oven_setup(无效){/ /设置输入和输出CONFIG_AS_OUTPUT(TRIAC_EN);CONFIG_AS_INPUT(ZERO_CROSS);/ /初始值输出SET_LOW(TRIAC_EN);/ /配置外部中断寄存器(最终进入macros.h)EICRA | = (1< < ISC01);/ /下降沿INT0生成一个IRQEIMSK | = (1< < INT0);/ /启用INT0外部中断屏蔽/ /启用定时器/溢出&当Counter2并触发中断/ /等于OC2ATIMSK2 | = (1< < OCIE2A) | (1< < TOIE2);}

这个函数设置GPIO和中断条件,以及启用定时器/ Counter2。

输出强度函数

无效oven_setDutyCycle(uint8_t百分比){uint16_tnewCounter;/ / 1和99之间的百分比包容性使用查找表来翻译一个线性/ /电力需求相角轴上一个位置如果(% >0& & % <One hundred.)_percent =pgm_read_byte(&powerLUT [% -1]);/ /计算新的计数器值newCounter = ((TICKS_PER_HALF_CYCLE - MARGIN_TICKS TRIAC_PULSE_TICKS) * (One hundred.- %))/One hundred.;/ /设置新的国家中断了,因为16位写不是原子cli();_counter_t2 = newCounter;_percent = %;sei();}

这个函数控制烤箱的输出功率,并相应地设置计时器等价值。的powerLUT []数组用于线性比例尺度映射到一个非线性曲线。线性范围内,实际输出功率变化在1%和2%之间或显著小于97%至98%,在50%到51%之间。这是由于季度波形的正弦性质我们变暗。这个重新映射——看到的查找表有助于正确更新1:改善相角时机更多信息。PROGMEM属性的地方整个数组到闪存内存,节省空间的实际项目。这将是有用的为常量字符串存储在以后的系列。

讨论二阶导数过零中断

ISR(INT0_vect) {/ * 0是一个开关。向上或向下一个百分比,流浪到*结束区,边缘宽足以满足最低脉冲宽度和延迟的零交叉射击* /如果(_percent = =0){OVEN_OFF();返回;}/ /用户要求100或calc轮高达100其他的如果(_percent = =One hundred.| | _counter_t2 = =0){OVEN_ON();}/ /一个常数相比是相当快其他的如果(_counter_t2 > TICKS_PER_HALF_CYCLE - TRIAC_PULSE_TICKS MARGIN_TICKS) {/ /还一个常数比较也非常快如果(_counter_t2 > (TICKS_PER_HALF_CYCLE - (TRIAC_PULSE_TICKS - MARGIN_TICKS /2))){/ /轮一半完全关闭OVEN_OFF();返回;}其他的_counter_t2 = TICKS_PER_HALF_CYCLE - TRIAC_PULSE_TICKS MARGIN_TICKS;}/ /计数器是可以接受的,或被四舍五入是可以接受的OCR2A = _counter_t2;TCNT2 =0;TCCR2B = (1< < CS20) | (1< < CS21) | (1< < CS22);/ /开始时间:8 mhz / 1024 = 128 /蜱虫}

这对销PD2的下降沿触发。这取决于全球_percent变量设置为,要么打开烤箱全部,全部,或设置定时器/ Counter2“输出比较寄存器”一个值对应于零交叉中断火灾后的“时间”。然后清除定时器/ Counter2并启动计时器。

定时器中断/ Counter2比较

ISR(TIMER2_COMPA_vect) {/ /打开烤箱,把它活跃一分钟闭锁时间切换OVEN_ON();/ /溢出中断时将火的最小脉冲宽度TCNT2 =256年——TRIAC_PULSE_TICKS;}

当输出值是比较满足,这个中断触发,它集TRIAC_ACTIVE销高和负载TCNT2 TRIAC_PULSE_TICKS计数后后注册,让它溢出。

定时器/ Counter2溢出中断

ISR(TIMER2_OVF_vect) {/ /关闭烤箱OVEN_OFF();/ /关闭定时器。零交点处理程序将重新启动它TCCR2B =0;}

当定时器溢出时,TRIAC_ACTIVE销低,定时器关闭,等待一个INT0_vect重复这个过程。

USART

在正常的C或c++编程电脑,功能assert ()sprintf ()可以格式化文本打印到终端,帮助调试。为了与我们的设备,我们需要实现一些打印到终端的方式。最简单的方法是通过串行通信接口的USART和usb串口转换器。

USART初始化函数

无效usart_setup(uint32_tubrr) {/ /设置波特率高和低字节的ubrr加载到UBRR0登记UBRR0H = (ubrr > >8);UBRR0L = ubrr;/ /打开传输和接收电路UCSR0B = (1< < RXCIE0) | (1< < RXEN0) | (1< < TXEN0);/ *设置帧格式:8数据,2停止位* /UCSR0C = (1<
           3 <
            / /使用8 - N - 1 - >八(8)数据位,没有(N) partiy比特,一(1)停止位/ /初始vlaue USCR0C是0 b00000110实现8 n1
            / /默认。设置这些比特是偏执的帕特里克和人
            / /喜欢reeeeeally确保硬件做你说的UCSR0C = (1< < UCSZ00) | (1< < UCSZ01);}
          

usart.c,有标准的usart_setup (uint32_t ubrr)初始化函数,使得硬件和建立了波特率(比特/秒)和传输设置(8位数据位,奇偶校验位,1停止位)。这是硬编码为现在的9600波特usart.h文件。

打印单个字节函数

无效usart_txb(常量字符数据){/ /等待空传输缓冲区(!(UCSR0A & (1< < UDRE0)));/ /将数据放入缓冲区,发送数据UDR0 =数据;}

这个函数接受一个字节发送缓冲区为空时,加载的字节缓冲区。这是其他印刷功能的基础。

印刷辅助函数

/ * * * USART打印字符串函数* * * /无效usart_print(常量字符*数据){(*数据! =\ 0)usart_txb(* + +数据);}
/ * * * USART打印字符串函数与新行和回车* * * /无效usart_println(常量字符*数据){usart_print(数据);usart_print(r \ n \);/ / GNU屏幕要求\ r \ n一样:(}

就像Arduino的并()和以()函数,这些需要一个字符串作为参数,为每一个字符,调用usart_txb ()函数。usart_println ()只有一个额外的步骤打印新行和回车。

在接收中断

ISR(USART_RX_vect) {无符号字符ReceivedByte;ReceivedByte = UDR0;UDR0 = ReceivedByte;}

现在没有办法通过USART——有意义与软件交互ISR (USART_RX_vect)是作为未来发展的一个占位符。从usb串口转换器接收一个角色时,触发一个中断,它反射相同的字符输出显示在屏幕上。

通用定时器

一般延迟时间和比较函数在很多单片机应用程序非常有用。的_delay ()函数< avr utils.h = " " > < / avr >有利于小延迟,因为它使用一个while循环和nop指令为指定的时间什么都不做。这可以防止其他发生在程序中,然而。处理测量长时间块,允许程序继续,我们使用一个免费的硬件定时器和中断。ATmega328P,定时器/ Counter0有点瘸的,没有尽可能多的功能的定时器/ Counter1和定时器/ Counter2所以这是一个小胜利能够使用一些有用的东西。我们仍然有T / C1但保存就好了在未来更复杂。

计时器Initization函数

无效msTimer_setup(无效){/ /让一切独自在TCCR0A设置预定标器Clk / 8/ /在TCCR0BTCCR0B | = (1< < CS01);/ /启用中断当定时器/ Counter0达到最大价值和溢出TIMSK0 | = (1< < TOIE0);}

第一个函数是当然的初始化函数。它将预定标器设置为1 MHz,使溢出中断。

返回当前系统时间的函数

uint32_tmsTimer_millis(无效){uint32_t女士;/ /注意:一个8位单片机不能自动读/写我们一个32位的值/ /必须禁用中断,同时避免一个检索值/ /写到一半的价值如果中断在当我们阅读它cli();女士= _ms_counter;sei();返回女士;}

msTimer函数链在一起,最终以某种方式调用这个函数。这个简单的返回全球的价值_ms_counter变量更新每一毫秒。

通用毫秒延迟函数

无效msTimer_delay(uint32_t等待){uint32_t目标;目标=msTimer_millis()+等待;(_ms_counter <目标);}

这是延迟()效用函数。它接受作为参数毫秒的数量你想等待和块而()循环,直到完成。这仍应只用于短延迟。

时差测量功能

uint32_tmsTimer_deltaT(uint32_t开始){/ /返回起始时间和现在,考虑/ /包着的uint32_t现在=msTimer_millis();如果(现在>开始)返回现在,开始;其他的返回现在+ (0 xffffffff -开始+1);}

措施之间时间增量开始时间和当前时间。可用于延迟循环不要阻塞。也占概括——因为时间是保存在一个32位uint32_t变量,当它达到0 xffffffff和增量,它回滚到零。这个因素,在计算。

超时检测功能

boolmsTimer_hasTimedOut(uint32_t开始,uint32_t超时){/ /检查是否超时已超过了。这是旨在应对包装/ /在返回msTimer_deltaT(开始)>超时;}

真或假国旗时抛出检查如果一定量的时间已经过去了。这是用于温度传感器,这样您就可以调用read ()函数在任何你想要的速度,但它只会更新根据其超时间隔。

定时器/ Counter0溢出中断

ISR(TIMER0_OVF_vect) {_ms_subCounter + +;如果= = ((_ms_subCounter & 0 x3)0)_ms_counter + +;TCNT0 + =6;}

ISR运行。非常准确地增加全球_ms_counter变量每一毫秒。

温度传感器

使用的函数和数据结构与MAX31855界面温度传感器比以往有点不同。我使用面向pseudo-object范式哪里有一个名叫max31855结构中定义max31855.h:

类型定义结构体max31855 {int16_textTemp;/ / 14-bit TC tempint16_tintTemp;/ / 12位内部温度uint8_t状态;/ /状态标志uint32_tlastTempTime;/ /“时间戳”uint32_tpollInterval;/ /刷新的速度传感器}max31855;

c,一个结构和一个指针指向它创建和任何时间温度需要阅读或USART需要打印的值,结构体指针作为参数传递到不同的功能。

温度传感器“对象”的构造函数

max31855 *max31855_setup(无效){/ /保留一些空间,确保它不是零max31855 * tempSense =malloc(运算符(max31855));断言(tempSense ! =);/ / Initilaize结构tempSense - > extTemp =0;tempSense - > intTemp =0;tempSense - >状态=未知;/ /不知道为什么安迪·布朗让他最后临时时间从0开始xffffd8ef但/ /工作原理……也许是为了测试timer0环绕/保证因果关系:/ / https://github.com/andysworkshop/awreflow2/blob/master/atmega8l/TemperatureSensor.htempSense - > lastTempTime = 0 xffffffff -10000年;tempSense - > pollInterval = DEFAULT_POLL_INTERVAL;/ /设置GPIO的方向CONFIG_AS_OUTPUT(MAX31855_CS);CONFIG_AS_OUTPUT(MAX31855_MOSI);CONFIG_AS_OUTPUT(MAX31855_SCK);CONFIG_AS_INPUT(MAX31855_MISO);/ /上启用停下~ CSPULLUP_ON(MAX31855_CS);/ /输出设置为默认值SET_HIGH(MAX31855_CS);SET_LOW(MAX31855_MOSI);SET_LOW(MAX31855_SCK);/ /启用SPI,主人,设置时钟频率fosc / 4(已经默认但我们/ /偏执帕特里克在这里也想明确我们的代码!)SPCR = (1< < SPE) | (1< < MSTR);SPCR & = ~ ((1< < SPR1) | (1< < SPR0));/ /没有必要............/ /超级速度2 x SPI时钟powerup !SPSR | = (1< < SPI2X);返回tempSense;}

这是“构造”和max31855结构体的初始化函数。它在内存中保留了空间使用malloc ()并确保它不是零。由于没有sprintf ()AVR的内置库默认情况下,如果条件为真,就中止程序,迫使它变成一个无休止的循环。然后配置GPIO SPI外围硬件。

读取和更新温度传感器功能

boolmax31855_readTempDone(max31855 * tempSense) {如果(msTimer_hasTimedOut(tempSense - > lastTempTime tempSense - > pollInterval)) {uint8_t我;/ /循环指数uint32_trawBits =0;/ /原始SPI总线位/ /把~ CS低SET_LOW(MAX31855_CS);/ /时钟SPI总线的4个字节(我=0;我<4;我+ +){SPDR =0;/ /开始“传输”(实际上只是时钟)(!(SPSR & (1< < SPIF)));/ /等待传输的目的rawBits < < =8;/ /字节腾出空间rawBits | = SPDR;/ /合并在新的字节}/ /恢复CS高SET_HIGH(MAX31855_CS);/ /解析出临时/从原始比特错误代码。是开关/ /语句坏?我不晓得。也许吧。谁在乎呢?uint8_td = rawBits &7;/ /有任何错误吗?如果(d) {tempSense - >状态=好;/ /只有当tempterature是有效的临时会更新。/ /摄氏整数,临时隔离&位掩码,发生了变化/ /向右对齐LSB (intTemp extTemp 18, 4),/ /再次转移到右摄氏度(extTemp = 0.25 c/ /位> > 2;> > 4)每比特intTemp = 0.0625摄氏度tempSense - > extTemp = rawBits > >20.;tempSense - > intTemp = (rawBits & 0 x0000fff0) > >8;/ /扩展符号位如果负值是阅读。在烤箱。哈!如果(tempSense - > extTemp & 0 x0800) tempSense - > extTemp | = 0 xf000;如果(tempSense - > intTemp & 0 x0020) tempSense - > intTemp | = 0 xffc0;}其他的{/ /设置临时工的东西显然是错误的tempSense - > extTemp =22222年;tempSense - > intTemp =11111年;/ /这是错误代码?开关(d) {情况下1:tempSense - >状态= OC_FAULT;打破;情况下2:tempSense - >状态= SCG_FAULT;打破;情况下4:tempSense - >状态= SCV_FAULT;打破;默认的:tempSense - >状态=未知;打破;}}/ /更新时间戳,让读循环疏通tempSense - > lastTempTime =msTimer_millis();返回真正的;}返回;}

只在定义的轮询间隔刷新,这个功能在很大程度上依赖msTimer_hasTimedOut ()函数。如果超时已满足,它在32位时钟SPI总线和读取的数据。如果阅读是有效的,没有任何错误的设置,它解析温度(内部参考和外部热电偶)到最近的整数。如果有一个错误,临时工将一些明显错误的和适当的设置状态标志。

状态消息Helper函数

常量字符*max31855_statusString(uint8_t状态){开关(状态){情况下无名:返回未知的;情况下好:返回好的!;情况下SCV_FAULT:返回SCV_FAULT;情况下SCG_FAULT:返回SCG_FAULT;情况下OC_FAULT:返回OC_FAULT;}返回犯错;}

基于状态码,返回一个字符串与USART印刷。

温度传感器打印功能

无效max31855_print(max31855 * tempSense) {/ / max (int16_t) =“65535”+“\ 0”字符缓冲(6]= {0};usart_print(状态:);usart_println(max31855_statusString(tempSense - >状态));usart_print(外部温度:);usart_println(itoa(tempSense - > extTemp缓冲区,10));usart_print(内部温度:);usart_println(itoa(tempSense - > intTemp缓冲区,10));}

二进制温度的值转换为十进制使用itoa ()功能和使用USART打印。

把它放在一起

c文件只是一个小测试文件,初始化所有的其他部分(设备)_setup命令,清除任何USART然后进入一个无限循环。在循环,它逐渐褪去了双向可控硅驱动强度在不断试图读取温度。因为有一个指定的轮询间隔max31855_readTempDone ()函数,它只会更新和打印状态,温度,速度。

/ * * *主要。c * * * /#包括上面int主要(无效){/ /全局禁用中断cli();/ /设置烤箱定时器,USART, SPIoven_setup();msTimer_setup();usart_setup(BAUD_PRESCALE);/ /有点像OOP的Cmax31855 * m =max31855_setup();/ /刷新USART缓冲区usart_flush();/ /清除中断标志通过阅读中断寄存器/ /指定的“未使用”,所以编译器并不抱怨uint8_t使用__attribute__(未使用的)= SPSR;假= SPDR;/ /打开全球中断标志sei();/ /启动消息“Hello World”usart_println(热烤面包机操作);/ /主程序循环(;;){/ /“消失”的责任与单周期循环int我=0;intdir =1;(我=0;我> - - -1;我= + dir) {/ /控制功率输出oven_setDutyCycle(我);/ /开关方向在高峰和暂停10 ms如果(我= =One hundred.)dir = -1;msTimer_delay(10);/ /如果是做阅读,打印临时和地位如果(max31855_readTempDone(m))max31855_print(m);}}返回1;}

最后编译和上传的代码,我们使用GNU使。允许您指定编译器和程序员选择有点神秘的语法。我借了makefile模板帕特Deegan electrons.psychogenic.com和修改它来满足我的需要。你可能需要做同样的如果你设置不同于我的。主要的事情你应该关心的是:

#目标控制器的名称#……单片机= atmega328p
# ID与程序员使用#……PROGRAMMER_MCU = atmega328p
#我们的项目名称#……PROJECTNAME = iot-reflow-oven
#程序员id #……AVRDUDE_PROGRAMMERID = dragon_isp
#端口#……AVRDUDE_PORT = usb

一旦一切都是你所喜欢的,类型使编译和sudo使writeflash上传你的董事会。如果一切按计划进行,应该是这样的:

结论

下一步是获得一个真正的烤面包机在混合并开始开发反馈控制。我们要进入一些控制理论在下一篇文章中,编写一些测试脚本描述系统的行为。这样我们可以创建一个健壮的、快速和可靠的控制器无论面对小扰动和烤箱不同类型。防范黑客!

atmega328p_2015 - 11月- 13. - zip

自己尝试这个项目!BOM。

1评论