├── .cproject
├── .gitignore
├── .project
├── .settings
└── language.settings.xml
├── .travis.yml
├── COMPILING.md
├── LICENSE
├── Makefile
├── README.md
├── T-962-controller.ld
├── logs
└── .gitignore
├── schematic-T962.pdf
├── serial-control.py
└── src
├── LPC214x.h
├── PID_v1.c
├── PID_v1.h
├── adc.c
├── adc.h
├── buzzer.c
├── buzzer.h
├── circbuffer.c
├── circbuffer.h
├── cr_startup_lpc21.s
├── crp.c
├── eeprom.c
├── eeprom.h
├── i2c.c
├── i2c.h
├── images
├── UEoSlogo-128x64.bmp
├── editprofile-18x64.bmp
├── f3edit-18x16.bmp
├── graph-128x64.bmp
├── selectprofile-18x64.bmp
└── stop-18x64.bmp
├── import.s
├── io.c
├── io.h
├── keypad.c
├── keypad.h
├── lcd.c
├── lcd.h
├── main.c
├── max31855.c
├── max31855.h
├── nvstorage.c
├── nvstorage.h
├── onewire.c
├── onewire.h
├── reflow.c
├── reflow.h
├── reflow_profiles.c
├── reflow_profiles.h
├── rtc.c
├── rtc.h
├── sc18is602b.c
├── sc18is602b.h
├── sched.c
├── sched.h
├── sensor.c
├── sensor.h
├── serial.c
├── serial.h
├── setup.c
├── setup.h
├── smallfont.h
├── systemfan.c
├── systemfan.h
├── t962.h
├── version.h
├── vic.c
└── vic.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 | *.ko
4 | *.obj
5 | *.elf
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Libraries
12 | *.lib
13 | *.a
14 | *.la
15 | *.lo
16 |
17 | # Shared objects (inc. Windows DLLs)
18 | *.dll
19 | *.so
20 | *.so.*
21 | *.dylib
22 |
23 | # Executables
24 | *.exe
25 | *.out
26 | *.app
27 | *.i*86
28 | *.x86_64
29 | *.hex
30 |
31 | Debug/
32 | Release/
33 | build
34 | lpc21isp
35 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | T-962-Controller
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder
10 | clean,full,incremental,
11 |
12 |
13 |
14 |
15 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
16 | full,incremental,
17 |
18 |
19 |
20 |
21 |
22 | org.eclipse.cdt.core.cnature
23 | org.eclipse.cdt.managedbuilder.core.managedBuildNature
24 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.settings/language.settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: c
2 |
3 | before_install:
4 | - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
5 | - sudo apt-get update -qq
6 |
7 | install:
8 | - sudo apt-get install gcc-arm-embedded
9 |
10 | script:
11 | - make all
12 |
13 | notifications:
14 | email: false
15 |
16 | git:
17 | depth: 10
18 |
19 | deploy:
20 | provider: releases
21 | api-key:
22 | secure: RYGBzgLnsjZ8JGjuZhor+lRVTvPYpm2QVccDyLf5Mr9cPhoOpULa6RhNkzdRAltJxoTlA7AS6l8ZpHIKyjMysFKC5pu8VJHWNdGvnrVURs4ML0qXUCPw2epDztTIdH2tMbkKI86Ov8EAmdvvR0GakEvkzC2z+rwPzGK95k3ovX4=
23 | file: "build/T-962-controller.hex"
24 | skip_cleanup: true
25 | on:
26 | tags: true
27 | all_branches: true
28 | repo: UnifiedEngineering/T-962-improvements
29 |
--------------------------------------------------------------------------------
/COMPILING.md:
--------------------------------------------------------------------------------
1 | # Compiling without LPCXpresso
2 |
3 | We need the `gcc-arm-none-eabi` compiler, for example in Ubuntu:
4 |
5 | ```
6 | sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded
7 | sudo apt-get update
8 | sudo apt-get install gcc-arm-none-eabi
9 | ```
10 |
11 | And then run
12 |
13 | ```
14 | make
15 | ```
16 |
17 | ## Flashing in Linux
18 |
19 | The makefile has a target to download and build the [lpc21isp utility from sourceforge](http://sourceforge.net/projects/lpc21isp/). Just run
20 |
21 | ```
22 | make flash
23 | ```
24 | and it wil be downloaded and compiled for you.
25 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Makefile to build the improved T-962 firmware without LPCXpresso
3 | #
4 | # Makes a 'build' directory in the root of the project.
5 | ################################################################################
6 | BASE_NAME := T-962-controller
7 |
8 | SRC_DIR := ./src/
9 | BUILD_DIR := ./build/
10 | TARGET := $(BUILD_DIR)$(BASE_NAME).axf
11 |
12 |
13 | vpath %.c $(SRC_DIR)
14 | vpath %.o $(BUILD_DIR)
15 | vpath %.d $(BUILD_DIR)
16 |
17 | CC := arm-none-eabi-gcc
18 | RM := rm -rf
19 |
20 | # Flash tool settings
21 | FLASH_TOOL := ./lpc21isp
22 | FLASH_TTY := /dev/ttyUSB0
23 | FLASH_BAUD := 57600
24 | MCU_CLOCK := 11059
25 |
26 | COLOR_GREEN = $(shell echo "\033[0;32m")
27 | COLOR_RED = $(shell echo "\033[0;31m")
28 | COLOR_END = $(shell echo "\033[0m")
29 |
30 | # Source files
31 | C_SRCS += $(wildcard $(SRC_DIR)*.c) $(BUILD_DIR)version.c
32 |
33 | S_SRCS += $(wildcard $(SRC_DIR)*.s)
34 |
35 | OBJS := $(patsubst $(SRC_DIR)%.c,$(BUILD_DIR)%.o,$(C_SRCS)) $(patsubst $(SRC_DIR)%.s,$(BUILD_DIR)%.o,$(S_SRCS))
36 |
37 | C_DEPS := $(wildcard *.d)
38 |
39 | all: axf
40 |
41 | $(BUILD_DIR)version.c: $(BUILD_DIR)tag
42 | git describe --tag --always --dirty | \
43 | sed 's/.*/const char* Version_GetGitVersion(void) { return "&"; }/' > $@
44 |
45 | # Always regenerate the git version
46 | .PHONY: $(BUILD_DIR)version.c
47 |
48 | $(BUILD_DIR)tag:
49 | mkdir -p $(BUILD_DIR)
50 | touch $(BUILD_DIR)tag
51 |
52 | $(BUILD_DIR)%.o: $(SRC_DIR)%.c $(BUILD_DIR)tag
53 | @echo 'Building file: $<'
54 | $(CC) -std=gnu99 -DNDEBUG -D__NEWLIB__ -Os -g -Wall -Wunused -c -fmessage-length=0 -fno-builtin -ffunction-sections -fdata-sections -flto -ffat-lto-objects -mcpu=arm7tdmi -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -MT"$(@:%.o=%.d)" -o "$@" "$<"
55 | @echo 'Finished building: $(COLOR_GREEN)$<$(COLOR_END)'
56 | @echo ' '
57 |
58 | $(BUILD_DIR)%.o: $(SRC_DIR)%.s $(BUILD_DIR)tag
59 | @echo 'Building file: $<'
60 | $(CC) -c -x assembler-with-cpp -I $(BUILD_DIR) -DNDEBUG -D__NEWLIB__ -mcpu=arm7tdmi -o "$@" "$<"
61 | @echo 'Finished building: $(COLOR_GREEN)$<$(COLOR_END)'
62 | @echo ' '
63 |
64 |
65 | axf: $(OBJS) $(USER_OBJS)
66 | @echo 'Building target: $@'
67 | @echo 'Invoking: MCU Linker'
68 | $(CC) -nostdlib -Xlinker -Map="$(BUILD_DIR)$(BASE_NAME).map" -Xlinker --gc-sections -flto -Os -mcpu=arm7tdmi --specs=nano.specs -u _printf_float -u _scanf_float -T "$(BASE_NAME).ld" -o "$(TARGET)" $(OBJS) $(USER_OBJS) $(LIBS)
69 | @echo 'Finished building target: $(COLOR_GREEN)$@$(COLOR_END)'
70 | @echo ' '
71 | $(MAKE) --no-print-directory post-build
72 |
73 | clean:
74 | -$(RM) $(BUILD_DIR)
75 | -@echo ' '
76 |
77 | post-build:
78 | -@echo 'Performing post-build steps'
79 | -arm-none-eabi-gcc --version
80 | -arm-none-eabi-size "$(TARGET)";
81 | -arm-none-eabi-objcopy -v -O ihex "$(TARGET)" "$(BUILD_DIR)$(BASE_NAME).hex"
82 | -@echo ' '
83 |
84 | lpc21isp: $(BUILD_DIR)tag
85 | -@echo ''
86 | -@echo 'Downloading lpc21isp 1.97 source from sourceforge'
87 | wget http://sourceforge.net/projects/lpc21isp/files/lpc21isp/1.97/lpc21isp_197.zip/download -O $(BUILD_DIR)lpc21isp.zip
88 | unzip -qq -o $(BUILD_DIR)lpc21isp.zip -d $(BUILD_DIR)
89 |
90 | -@echo 'Making lpc21isp'
91 | $(MAKE) -C $(BUILD_DIR)lpc21isp_197/
92 | -@echo 'Copy lpc21isp binary to current directory'
93 | cp $(BUILD_DIR)lpc21isp_197/lpc21isp .
94 | -@echo ''
95 |
96 | flash: axf lpc21isp
97 | @echo ''
98 | @echo 'Flashing $(COLOR_GREEN)$(BASE_NAME).hex$(COLOR_END) to $(COLOR_RED)$(FLASH_TTY)$(COLOR_END)'
99 | $(FLASH_TOOL) "$(BUILD_DIR)$(BASE_NAME).hex" $(FLASH_TTY) $(FLASH_BAUD) $(MCU_CLOCK)
100 |
101 | .PHONY: clean dependents
102 | .SECONDARY: post-build
103 |
104 | -include ../makefile.targets
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | T-962 reflow oven improvements
2 | ==============================
3 | Custom firmware for the cheap T-962 reflow oven utilizing the _existing_ controller hardware.
4 |
5 | - [Wiki] for more info
6 | - [Hackaday post]
7 | - We have [Travis-CI] in place to build pull requests
8 |
9 | ### Introduction
10 |
11 | As we had use for a small reflow oven for a small prototype run we settled for the `T-962` even after having seen the negative reviews of it as there were plenty of suggestions all across the Internet on how it could be improved including replacing the existing controller and display(!). After having had a closer look at the hardware (replacing the masking tape inside with Kapton tape first) it was obvious that there was a simple way to improve the software disaster that is the T-962.
12 |
13 |
14 | ### Hardware improvements
15 |
16 | Here are a few improvements made to the cheap T-962 reflow oven utilizing the _existing_ controller hardware with only a small, cheap, but very necessary modification. As you have to open the top part of the oven anyway to reflash the software this is a no-brainer fix:
17 |
18 | #### Replace stinky masking tape
19 |
20 | Instructable suggesting [replacing masking tape with kapton tape](http://www.instructables.com/id/T962A-SMD-Reflow-Oven-FixHack/?ALLSTEPS).
21 |
22 | #### Cold junction compensation
23 |
24 | The existing controller makes the assumption that the cold-junction is at 20 degrees Celsius at all times which made keeping a constant temperature "a bit" challenging as the terminal block sits _on_top_of_an_oven_ with two TRIACs nearby.
25 | We can fix this by adding a temperature sensor to the connector block where the thermocouples are connected to the controller board.
26 | It turns out that both an analog input and at least one generic GPIO pin is available on unpopulated pads on the board. GPIO0.7 in particular was very convenient for 1-wire operation as there was an adjacent pad with 3.3V so a 4k7 pull-up resistor could be placed there, then a jumper wire is run from GPIO0.7 pad to the `Dq` pin of a cheap [DS18B20] 1-wire temperature sensor that gets epoxied to the terminal block, soldering both `Vcc` and ground pins to the ground plane conveniently located right next to it. Some hot-glue may have to be removed to actually get to the side of the connector and the ground plane, someone seems to have been really trigger-happy with the glue gun!
27 |
28 | [Wiki: cold junction compensation mod](https://github.com/UnifiedEngineering/T-962-improvements/wiki)
29 |
30 |
31 | #### Check mains earth connection
32 |
33 | As mentioned elsewhere, make sure the protective earth/ground wire from the main input actually makes contact with the back panel of the chassis and also that the back panel makes contact both with the top and bottom halves of the oven!
34 |
35 | #### System fan PWM control
36 |
37 | The system fan is very noisy an can be turned of most of the time. The custom firmware uses spare `ADO` test point to control it.
38 |
39 | [Wiki: system fan PWM mod](https://github.com/UnifiedEngineering/T-962-improvements/wiki/System-fan-control)
40 |
41 | ### New firmware
42 |
43 | The firmware was originally built with LPCXpresso 7.5.0 as I've never dealt with the LPC2000-series NXP microcontrollers before so I just wanted something that wouldn't require TOO much of work to actually produce a flashable image. Philips LPC2000 Flash Utility v2.2.3 was used to flash the controller through the ISP header present on the board.
44 |
45 | LPCXpresso requires activation but is free for everything but large code sizes (the limit is larger than the 128kB flash size on this controller anyway so it's not really an issue). The flash utility unfortunately only runs on Windows but Flash Magic is an alternative (see Wiki for more flashing instructions).
46 |
47 | With help from the community the project now also builds standalone using the standard `gcc-arm-none-eabi` toolchain, see `COMPILING.md` for more information.
48 |
49 | The MCU in this particular oven is an LPC2134/01 with 128kB flash/16kB RAM, stated to be capable of running at up to 60MHz. Unfortunately the PLL in this chip is not that clever so with the supplied XTAL at 11.0592MHz we can only reach 55.296MHz (5x multiplier). Other variants exist, the [Wiki] has more information about this.
50 |
51 | wiki: [Flashing firmware]
52 |
53 | ### Contributing
54 | This is mainly tested on a fairly recent build of the T-962 (smallest version), build time on the back panel states 14.07 which I assume means 2014 July (or less likely week 7 of 2014), success/failure reports from other users are welcome!
55 |
56 | This is very much a quick hack to get only the basic functionality needed up and running. Everything in here is released under the GPLv3 license in hopes that it might be interesting for others to improve on this. Feedback is welcome!
57 |
58 | Happy hacking!
59 |
60 | # Acknowledgements
61 | This project is using the [C PID Library - Version 1.0.1, GPLv3]
62 |
63 | [wiki]: https://github.com/UnifiedEngineering/T-962-improvements/wiki
64 | [Travis-CI]: https://travis-ci.org/UnifiedEngineering/T-962-improvements
65 | [Flashing firmware]: https://github.com/UnifiedEngineering/T-962-improvements/wiki/Flashing-the-LPC21xx-controller
66 | [DS18B20]: http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
67 | [hackaday post]: http://hackaday.com/2014/11/27/improving-the-t-962-reflow-oven/
68 | [C PID Library - Version 1.0.1, GPLv3]:https://github.com/mblythe86/C-PID-Library
69 |
--------------------------------------------------------------------------------
/T-962-controller.ld:
--------------------------------------------------------------------------------
1 | /**
2 | * Linker script for the T-962 controller improved firmware.
3 | *
4 | * Amended from a version generated by LPCXpresso v7.5.0
5 | */
6 |
7 | GROUP(
8 | libgcc.a
9 | libc_nano.a
10 | libm.a
11 | libnosys.a
12 | )
13 |
14 | MEMORY {
15 | /* Define each memory region */
16 | MFlash128 (rx) : ORIGIN = 0x0, LENGTH = 0x20000 /* 128K bytes */
17 | Ram16 (rwx) : ORIGIN = 0x40000000, LENGTH = 0x4000 /* 16K bytes */
18 | }
19 | /* Define a symbol for the top of each memory region */
20 | __top_MFlash128 = 0x0 + 0x20000;
21 | __top_Ram16 = 0x40000000 + 0x4000;
22 |
23 | ENTRY(_start)
24 |
25 | SECTIONS
26 | {
27 |
28 | /* MAIN TEXT SECTION */
29 | .text : ALIGN(4)
30 | {
31 | FILL(0xff)
32 | __vectors_start__ = ABSOLUTE(.) ;
33 | KEEP(*(.isr_vector))
34 |
35 | /* Global Section Table */
36 | . = ALIGN(4) ;
37 | __section_table_start = .;
38 | __data_section_table = .;
39 | LONG(LOADADDR(.data));
40 | LONG( ADDR(.data));
41 | LONG( SIZEOF(.data));
42 | __data_section_table_end = .;
43 | __bss_section_table = .;
44 | LONG( ADDR(.bss));
45 | LONG( SIZEOF(.bss));
46 | __bss_section_table_end = .;
47 | __section_table_end = . ;
48 | /* End of Global Section Table */
49 |
50 |
51 | *(.after_vectors*)
52 |
53 | /* Code Read Protect data */
54 | . = 0x000001FC ;
55 | PROVIDE(__CRP_WORD_START__ = .) ;
56 | KEEP(*(.crp))
57 | PROVIDE(__CRP_WORD_END__ = .) ;
58 | /*
59 | Disabled: We're using newlib, not redlib.
60 | ASSERT(!(__CRP_WORD_START__ == __CRP_WORD_END__), "Linker CRP Enabled, but no CRP_WORD provided within application"); */
61 | /* End of Code Read Protect */
62 |
63 | } >MFlash128
64 |
65 | .text : ALIGN(4)
66 | {
67 | *(.text*)
68 | *(.rodata .rodata.* .constdata .constdata.*)
69 | . = ALIGN(4);
70 |
71 | } > MFlash128
72 |
73 | /*
74 | * for exception handling/unwind - some Newlib functions (in common
75 | * with C++ and STDC++) use this.
76 | */
77 | .ARM.extab : ALIGN(4)
78 | {
79 | *(.ARM.extab* .gnu.linkonce.armextab.*)
80 | } > MFlash128
81 | __exidx_start = .;
82 |
83 | .ARM.exidx : ALIGN(4)
84 | {
85 | *(.ARM.exidx* .gnu.linkonce.armexidx.*)
86 | } > MFlash128
87 | __exidx_end = .;
88 |
89 | _etext = .;
90 |
91 |
92 | /* MAIN DATA SECTION */
93 |
94 |
95 | .uninit_RESERVED : ALIGN(4)
96 | {
97 | KEEP(*(.bss.$RESERVED*))
98 | . = ALIGN(4) ;
99 | _end_uninit_RESERVED = .;
100 | } > Ram16
101 |
102 |
103 | /* Main DATA section (Ram16) */
104 | .data : ALIGN(4)
105 | {
106 | FILL(0xff)
107 | _data = . ;
108 | *(vtable)
109 | *(.ramfunc*)
110 | *(.data*)
111 | . = ALIGN(4) ;
112 | _edata = . ;
113 | } > Ram16 AT>MFlash128
114 |
115 |
116 | /* MAIN BSS SECTION */
117 | .bss : ALIGN(4)
118 | {
119 | _bss = .;
120 | *(.bss*)
121 | *(COMMON)
122 | . = ALIGN(4) ;
123 | _ebss = .;
124 | PROVIDE(end = .);
125 | } > Ram16
126 |
127 |
128 | /* DEFAULT NOINIT SECTION */
129 | .noinit (NOLOAD): ALIGN(4)
130 | {
131 | _noinit = .;
132 | *(.noinit*)
133 | . = ALIGN(4) ;
134 | _end_noinit = .;
135 | } > Ram16
136 |
137 | PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
138 | PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_Ram16 - 0);
139 | }
140 |
--------------------------------------------------------------------------------
/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *.csv
2 | *.png
3 | *.pdf
4 |
--------------------------------------------------------------------------------
/schematic-T962.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/schematic-T962.pdf
--------------------------------------------------------------------------------
/serial-control.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Log the temperatures reported by the oven in a live plot and
5 | # in a CSV file.
6 | #
7 | # Requires
8 | # python 2.7
9 | # - pyserial (python-serial in ubuntu, pip install pyserial)
10 | # - matplotlib (python-matplotlib in ubuntu, pip install matplotlib)
11 | #
12 |
13 | import csv
14 | import datetime
15 | import matplotlib.pyplot as plt
16 | import matplotlib.gridspec as gridspec
17 | import serial
18 | import sys
19 | from time import time
20 |
21 | # settings
22 | #
23 | FIELD_NAMES = 'Time,Temp0,Temp1,Temp2,Temp3,Set,Actual,Heat,Fan,ColdJ,Mode'
24 | TTYs = ('/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2')
25 | BAUD_RATE = 115200
26 |
27 | logdir = 'logs/'
28 |
29 | MAX_X = 470
30 | MAX_Y_temperature = 300
31 | MAX_Y_pwm = 260
32 | #
33 | # end of settings
34 |
35 | def timestamp(dt=None):
36 | if dt is None:
37 | dt = datetime.datetime.now()
38 |
39 | return dt.strftime('%Y-%m-%d-%H%M%S')
40 |
41 |
42 | def logname(filetype, profile):
43 | return '%s%s-%s.%s' % (
44 | logdir,
45 | timestamp(),
46 | profile.replace(' ', '_').replace('/', '_'),
47 | filetype
48 | )
49 |
50 |
51 | def get_tty():
52 | for devname in TTYs:
53 | try:
54 | port = serial.Serial(devname, baudrate=BAUD_RATE)
55 | print 'Using serial port %s' % port.name
56 | return port
57 |
58 | except:
59 | print 'Tried serial port %s, but failed.' % str(devname)
60 | pass
61 |
62 | return None
63 |
64 |
65 | class Line(object):
66 | def __init__(self, axis, key, label=None):
67 | self.xvalues = []
68 | self.yvalues = []
69 |
70 | self._key = key
71 | self._line, = axis.plot(self.xvalues, self.yvalues, label=label or key)
72 |
73 | def add(self, log):
74 | self.xvalues.append(log['Time'])
75 | self.yvalues.append(log[self._key])
76 |
77 | self.update()
78 |
79 | def update(self):
80 | self._line.set_data(self.xvalues, self.yvalues)
81 |
82 | def clear(self):
83 | self.xvalues = []
84 | self.yvalues = []
85 |
86 | self.update()
87 |
88 | class Log(object):
89 | profile = ''
90 | last_action = None
91 |
92 | def __init__(self):
93 | self.init_plot()
94 | self.clear_logs()
95 |
96 | def clear_logs(self):
97 | self.raw_log = []
98 | map(Line.clear, self.lines)
99 | self.mode = ''
100 |
101 | def init_plot(self):
102 | plt.ion()
103 |
104 | gs = gridspec.GridSpec(2, 1, height_ratios=(4, 1))
105 | fig = plt.figure(figsize=(14, 10))
106 |
107 | axis_upper = fig.add_subplot(gs[0])
108 | axis_lower = fig.add_subplot(gs[1])
109 | plt.subplots_adjust(hspace=0.05, top=0.95, bottom=0.05, left=0.05, right=0.95)
110 |
111 | # setup axis for upper graph (temperature values)
112 | axis_upper.set_ylabel(u'Temperature [°C]')
113 | axis_upper.set_xlim(0, MAX_X)
114 | axis_upper.set_xticklabels([])
115 | axis_upper.set_ylim(0, MAX_Y_temperature)
116 |
117 | # setup axis for lower graph (PWM values)
118 | axis_lower.set_xlim(0, MAX_X)
119 | axis_lower.set_ylim(0, MAX_Y_pwm)
120 | axis_lower.set_ylabel('PWM value')
121 | axis_lower.set_xlabel('Time [s]')
122 |
123 | # select values to be plotted
124 | self.lines = [
125 | Line(axis_upper, 'Actual'),
126 | Line(axis_upper, 'Temp0'),
127 | Line(axis_upper, 'Temp1'),
128 | Line(axis_upper, 'Set', u'Setpoint'),
129 | Line(axis_upper, 'ColdJ', u'Coldjunction'),
130 | # Line(axis_upper, 'Temp2'),
131 | # Line(axis_upper, 'Temp3'),
132 |
133 | Line(axis_lower, 'Fan'),
134 | Line(axis_lower, 'Heat', 'Heater')
135 | ]
136 |
137 | axis_upper.legend()
138 | axis_lower.legend()
139 | plt.draw()
140 |
141 | self.axis_upper = axis_upper
142 | self.axis_lower = axis_lower
143 |
144 | def save_logfiles(self):
145 | print 'Saved log in %s ' % logname('csv', self.profile)
146 | plt.savefig(logname('png', self.profile))
147 | plt.savefig(logname('pdf', self.profile))
148 |
149 | with open(logname('csv', self.profile), 'w+') as csvout:
150 | writer = csv.DictWriter(csvout, FIELD_NAMES.split(','))
151 | writer.writeheader()
152 |
153 | for l in self.raw_log:
154 | writer.writerow(l)
155 |
156 | def parse(self, line):
157 | values = map(str.strip, line.split(','))
158 | # Convert all values to float, except the mode
159 | values = map(float, values[0:-1]) + [values[-1], ]
160 |
161 | fields = FIELD_NAMES.split(',')
162 | if len(values) != len(fields):
163 | raise ValueError('Expected %d fields, found %d' % (len(fields), len(values)))
164 |
165 | return dict(zip(fields, values))
166 |
167 | def process_log(self, logline):
168 | # ignore 'comments'
169 | if logline.startswith('#'):
170 | print logline
171 | return
172 |
173 | # parse Profile name
174 | if logline.startswith('Starting reflow with profile: '):
175 | self.profile = logline[30:].strip()
176 | return
177 |
178 | if logline.startswith('Selected profile'):
179 | self.profile = logline[20:].strip()
180 | return
181 |
182 | try:
183 | log = self.parse(logline)
184 | except ValueError, e:
185 | if len(logline) > 0:
186 | print '!!', logline
187 | return
188 |
189 | if 'Mode' in log:
190 | # clean up log before starting reflow
191 | if self.mode == 'STANDBY' and log['Mode'] in ('BAKE', 'REFLOW'):
192 | self.clear_logs()
193 |
194 | # save png graph an csv file when bake or reflow ends.
195 | if self.mode in ('BAKE', 'REFLOW') and log['Mode'] == 'STANDBY':
196 | self.save_logfiles()
197 |
198 | self.mode = log['Mode']
199 | if log['Mode'] == 'BAKE':
200 | self.profile = 'bake'
201 |
202 | if log['Mode'] in ('REFLOW', 'BAKE'):
203 | self.last_action = time()
204 | self.axis_upper.set_title('Profile: %s Mode: %s ' % (self.profile, self.mode))
205 |
206 | if 'Time' in log and log['Time'] != 0.0:
207 | if 'Actual' not in log:
208 | return
209 |
210 | # update all lines
211 | map(lambda x: x.add(log), self.lines)
212 | self.raw_log.append(log)
213 |
214 | # update view
215 | plt.draw()
216 |
217 | def isdone(self):
218 | return (
219 | self.last_action is not None and
220 | time() - self.last_action > 5
221 | )
222 |
223 |
224 | def loop_all_profiles(num_profiles=6):
225 | log = Log()
226 |
227 | with get_tty() as port:
228 | profile = 0
229 | def select_profile(profile):
230 | port.write('stop\n')
231 | port.write('select profile %d\n' % profile)
232 | port.write('reflow\n')
233 |
234 | select_profile(profile)
235 |
236 | while True:
237 | logline = port.readline().strip()
238 |
239 | if log.isdone():
240 | log.last_action = None
241 | profile += 1
242 | if profile > 6:
243 | print 'Done.'
244 | sys.exit()
245 | select_profile(profile)
246 |
247 | log.process_log(logline)
248 |
249 | def logging_only():
250 | log = Log()
251 |
252 | with get_tty() as port:
253 | while True:
254 | log.process_log(port.readline().strip())
255 |
256 | if __name__ == '__main__':
257 | action = sys.argv[1] if len(sys.argv) > 1 else 'log'
258 |
259 | if action == 'log':
260 | print 'Logging reflow sessions...'
261 | logging_only()
262 |
263 | elif action == 'test':
264 | print 'Looping over all profiles'
265 | loop_all_profiles()
266 | else:
267 | print 'Unknown action', action
268 |
--------------------------------------------------------------------------------
/src/PID_v1.c:
--------------------------------------------------------------------------------
1 | /**********************************************************************************************
2 | * C PID Library - Version 1.0.1
3 | * modified my Matthew Blythe mjblythe.com/hacks
4 | * originally by Brett Beauregard brettbeauregard.com
5 | *
6 | * This Library is licensed under a GPLv3 License
7 | **********************************************************************************************/
8 |
9 | #include "PID_v1.h"
10 | void PID_Initialize(PidType* pid);
11 |
12 | /*Constructor (...)*********************************************************
13 | * The parameters specified here are those for for which we can't set up
14 | * reliable defaults, so we need to have the user set them.
15 | ***************************************************************************/
16 | void PID_init(PidType* pid, FloatType Kp, FloatType Ki, FloatType Kd,
17 | PidDirectionType ControllerDirection) {
18 | pid->myInput = 0;
19 | pid->myOutput = 0;
20 | pid->mySetpoint = 0;
21 | pid->ITerm = 0;
22 | pid->lastInput = 0;
23 | pid->inAuto = false;
24 |
25 | PID_SetOutputLimits(pid, 0, 0xffff);
26 |
27 | //default Controller Sample Time is 0.1 seconds
28 | pid->SampleTime = 100;
29 |
30 | PID_SetControllerDirection(pid, ControllerDirection);
31 | PID_SetTunings(pid, Kp, Ki, Kd);
32 |
33 | // pid->lastTime = millis() - pid->SampleTime;
34 | }
35 |
36 |
37 | /* Compute() **********************************************************************
38 | * This, as they say, is where the magic happens. this function should be called
39 | * every time "void loop()" executes. the function will decide for itself whether a new
40 | * pid Output needs to be computed. returns true when the output is computed,
41 | * false when nothing has been done.
42 | **********************************************************************************/
43 | bool PID_Compute(PidType* pid) {
44 | if (!pid->inAuto) {
45 | return false;
46 | }
47 | // unsigned long now = millis();
48 | // unsigned long timeChange = (now - pid->lastTime);
49 | // if (timeChange >= pid->SampleTime) {
50 | /*Compute all the working error variables*/
51 | FloatType input = pid->myInput;
52 | FloatType error = pid->mySetpoint - input;
53 | pid->ITerm += (pid->ki * error);
54 | if (pid->ITerm > pid->outMax)
55 | pid->ITerm = pid->outMax;
56 | else if (pid->ITerm < pid->outMin)
57 | pid->ITerm = pid->outMin;
58 | FloatType dInput = (input - pid->lastInput);
59 |
60 | /*Compute PID Output*/
61 | FloatType output = pid->kp * error + pid->ITerm - pid->kd * dInput;
62 |
63 | if (output > pid->outMax)
64 | output = pid->outMax;
65 | else if (output < pid->outMin)
66 | output = pid->outMin;
67 | pid->myOutput = output;
68 |
69 | /*Remember some variables for next time*/
70 | pid->lastInput = input;
71 | // pid->lastTime = now;
72 | return true;
73 | // } else {
74 | // return false;
75 | // }
76 | }
77 |
78 |
79 | /* SetTunings(...)*************************************************************
80 | * This function allows the controller's dynamic performance to be adjusted.
81 | * it's called automatically from the constructor, but tunings can also
82 | * be adjusted on the fly during normal operation
83 | ******************************************************************************/
84 |
85 | void PID_SetTunings(PidType* pid, FloatType Kp, FloatType Ki, FloatType Kd) {
86 | if (Kp < 0 || Ki < 0 || Kd < 0){
87 | return;
88 | }
89 |
90 | pid->dispKp = Kp;
91 | pid->dispKi = Ki;
92 | pid->dispKd = Kd;
93 |
94 | FloatType SampleTimeInSec = ((FloatType) pid->SampleTime) / 1000;
95 | pid->kp = Kp;
96 | pid->ki = Ki * SampleTimeInSec;
97 | pid->kd = Kd / SampleTimeInSec;
98 |
99 | if (pid->controllerDirection == PID_Direction_Reverse) {
100 | pid->kp = (0 - pid->kp);
101 | pid->ki = (0 - pid->ki);
102 | pid->kd = (0 - pid->kd);
103 | }
104 | }
105 |
106 | /* SetSampleTime(...) *********************************************************
107 | * sets the period, in Milliseconds, at which the calculation is performed
108 | ******************************************************************************/
109 | void PID_SetSampleTime(PidType* pid, int NewSampleTime) {
110 | if (NewSampleTime > 0) {
111 | FloatType ratio = (FloatType) NewSampleTime / (FloatType) pid->SampleTime;
112 | pid->ki *= ratio;
113 | pid->kd /= ratio;
114 | pid->SampleTime = (unsigned long) NewSampleTime;
115 | }
116 | }
117 |
118 | /* SetOutputLimits(...)****************************************************
119 | * This function will be used far more often than SetInputLimits. while
120 | * the input to the controller will generally be in the 0-1023 range (which is
121 | * the default already,) the output will be a little different. maybe they'll
122 | * be doing a time window and will need 0-8000 or something. or maybe they'll
123 | * want to clamp it from 0-125. who knows. at any rate, that can all be done
124 | * here.
125 | **************************************************************************/
126 | void PID_SetOutputLimits(PidType* pid, FloatType Min, FloatType Max) {
127 | if (Min >= Max) {
128 | return;
129 | }
130 | pid->outMin = Min;
131 | pid->outMax = Max;
132 |
133 | if (pid->inAuto) {
134 | if (pid->myOutput > pid->outMax) {
135 | pid->myOutput = pid->outMax;
136 | } else if (pid->myOutput < pid->outMin) {
137 | pid->myOutput = pid->outMin;
138 | }
139 |
140 | if (pid->ITerm > pid->outMax) {
141 | pid->ITerm = pid->outMax;
142 | } else if (pid->ITerm < pid->outMin) {
143 | pid->ITerm = pid->outMin;
144 | }
145 | }
146 | }
147 |
148 | /* SetMode(...)****************************************************************
149 | * Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
150 | * when the transition from manual to auto occurs, the controller is
151 | * automatically initialized
152 | ******************************************************************************/
153 | void PID_SetMode(PidType* pid, PidModeType Mode)
154 | {
155 | bool newAuto = (Mode == PID_Mode_Automatic);
156 | if(newAuto == !pid->inAuto)
157 | { /*we just went from manual to auto*/
158 | PID_Initialize(pid);
159 | }
160 | pid->inAuto = newAuto;
161 | }
162 |
163 | /* Initialize()****************************************************************
164 | * does all the things that need to happen to ensure a bumpless transfer
165 | * from manual to automatic mode.
166 | ******************************************************************************/
167 | void PID_Initialize(PidType* pid) {
168 | pid->ITerm = pid->myOutput;
169 | pid->lastInput = pid->myInput;
170 | if (pid->ITerm > pid->outMax) {
171 | pid->ITerm = pid->outMax;
172 | } else if (pid->ITerm < pid->outMin) {
173 | pid->ITerm = pid->outMin;
174 | }
175 | }
176 |
177 | /* SetControllerDirection(...)*************************************************
178 | * The PID will either be connected to a DIRECT acting process (+Output leads
179 | * to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to
180 | * know which one, because otherwise we may increase the output when we should
181 | * be decreasing. This is called from the constructor.
182 | ******************************************************************************/
183 | void PID_SetControllerDirection(PidType* pid, PidDirectionType Direction) {
184 | if (pid->inAuto && Direction != pid->controllerDirection) {
185 | pid->kp = (0 - pid->kp);
186 | pid->ki = (0 - pid->ki);
187 | pid->kd = (0 - pid->kd);
188 | }
189 | pid->controllerDirection = Direction;
190 | }
191 |
192 | /* Status Funcions*************************************************************
193 | * Just because you set the Kp=-1 doesn't mean it actually happened. these
194 | * functions query the internal state of the PID. they're here for display
195 | * purposes. this are the functions the PID Front-end uses for example
196 | ******************************************************************************/
197 | FloatType PID_GetKp(PidType* pid) {
198 | return pid->dispKp;
199 | }
200 | FloatType PID_GetKi(PidType* pid) {
201 | return pid->dispKi;
202 | }
203 | FloatType PID_GetKd(PidType* pid) {
204 | return pid->dispKd;
205 | }
206 | PidModeType PID_GetMode(PidType* pid) {
207 | return pid->inAuto ? PID_Mode_Automatic : PID_Mode_Manual;
208 | }
209 | PidDirectionType PID_GetDirection(PidType* pid) {
210 | return pid->controllerDirection;
211 | }
212 |
--------------------------------------------------------------------------------
/src/PID_v1.h:
--------------------------------------------------------------------------------
1 | #ifndef PID_H
2 | #define PID_H
3 |
4 | typedef float FloatType;
5 | //typedef double floatType;
6 | #include
7 |
8 | //Constants used in some of the functions below
9 | typedef enum
10 | {
11 | PID_Mode_Automatic = 1,
12 | PID_Mode_Manual = 0
13 | } PidModeType;
14 |
15 | typedef enum
16 | {
17 | PID_Direction_Direct = 0,
18 | PID_Direction_Reverse = 1
19 | } PidDirectionType;
20 |
21 | typedef struct {
22 | FloatType dispKp; // * we'll hold on to the tuning parameters in user-entered
23 | FloatType dispKi; // format for display purposes
24 | FloatType dispKd; //
25 |
26 | FloatType kp; // * (P)roportional Tuning Parameter
27 | FloatType ki; // * (I)ntegral Tuning Parameter
28 | FloatType kd; // * (D)erivative Tuning Parameter
29 |
30 | PidDirectionType controllerDirection;
31 |
32 | FloatType myInput; // * Pointers to the Input, Output, and Setpoint variables
33 | FloatType myOutput; // This creates a hard link between the variables and the
34 | FloatType mySetpoint; // PID, freeing the user from having to constantly tell us
35 | // what these values are. with pointers we'll just know.
36 |
37 | // unsigned long lastTime;
38 | FloatType ITerm, lastInput;
39 |
40 | unsigned long SampleTime;
41 | FloatType outMin, outMax;
42 | bool inAuto;
43 | } PidType;
44 |
45 | //commonly used functions **************************************************************************
46 |
47 | // constructor. links the PID to the Input, Output, and
48 | // Setpoint. Initial tuning parameters are also set here
49 | void PID_init(PidType* pid,
50 | FloatType kp,
51 | FloatType ki,
52 | FloatType kd,
53 | PidDirectionType controllerDirection);
54 |
55 | // sets PID to either Manual (0) or Auto (non-0)
56 | void PID_SetMode(PidType* pid, PidModeType mode);
57 |
58 | // performs the PID calculation. it should be
59 | // called every time loop() cycles. ON/OFF and
60 | // calculation frequency can be set using SetMode
61 | // SetSampleTime respectively
62 | bool PID_Compute(PidType* pid);
63 |
64 | // clamps the output to a specific range. 0-255 by default, but
65 | // it's likely the user will want to change this depending on
66 | // the application
67 | void PID_SetOutputLimits(PidType* pid, FloatType min, FloatType max);
68 |
69 | //available but not commonly used functions ********************************************************
70 |
71 | // While most users will set the tunings once in the
72 | // constructor, this function gives the user the option
73 | // of changing tunings during runtime for Adaptive control
74 | void PID_SetTunings(PidType* pid, FloatType kp, FloatType ki, FloatType kd);
75 |
76 | // Sets the Direction, or "Action" of the controller. DIRECT
77 | // means the output will increase when error is positive. REVERSE
78 | // means the opposite. it's very unlikely that this will be needed
79 | // once it is set in the constructor.
80 | void PID_SetControllerDirection(PidType* pid, PidDirectionType Direction);
81 |
82 | // sets the frequency, in Milliseconds, with which
83 | // the PID calculation is performed. default is 100
84 | void PID_SetSampleTime(PidType* pid, int newSampleTime);
85 |
86 | //Display functions ****************************************************************
87 | // These functions query the pid for interal values.
88 | // they were created mainly for the pid front-end,
89 | // where it's important to know what is actually
90 | // inside the PID.
91 | FloatType PID_GetKp(PidType* pid);
92 | FloatType PID_GetKi(PidType* pid);
93 | FloatType PID_GetKd(PidType* pid);
94 | PidModeType PID_GetMode(PidType* pid);
95 | PidDirectionType PID_GetDirection(PidType* pid);
96 |
97 | //void PID_Initialize(PidType* pid);
98 | #endif
99 |
--------------------------------------------------------------------------------
/src/adc.c:
--------------------------------------------------------------------------------
1 | /*
2 | * adc.c - AD converter interface for T-962 reflow controller
3 | *
4 | * Copyright (C) 2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include
22 | #include
23 | #include "adc.h"
24 | #include "t962.h"
25 | #include "sched.h"
26 |
27 | #define NUM_ADCH (2)
28 | #define OVERSAMPLING_BITS (4) // This is n (how many additional bits to supply)
29 | #define NUM_ACCUM (256) // This needs to be 4 ^ n
30 | #define ACCUM_MASK (NUM_ACCUM-1)
31 |
32 | static uint32_t ADres[NUM_ADCH];
33 | static uint16_t ADidx[NUM_ADCH];
34 | static uint16_t ADaccum[NUM_ADCH][NUM_ACCUM];
35 |
36 | static void ADC_Accum(uint32_t chnum, uint32_t value) {
37 | uint16_t temp;
38 | chnum--;
39 | uint8_t idx = ADidx[chnum];
40 |
41 | // Done
42 | if (value & (1 << 31)) {
43 | temp = (value >> 6) & 0x3ff;
44 | ADres[chnum] -= ADaccum[chnum][idx];
45 | ADaccum[chnum][idx] = temp;
46 | ADres[chnum] += temp;
47 | idx++;
48 | idx &= ACCUM_MASK;
49 | ADidx[chnum] = idx;
50 | }
51 | }
52 |
53 | static int32_t ADC_Work(void) {
54 | ADC_Accum( 1, AD0DR1);
55 | ADC_Accum( 2, AD0DR2);
56 | return TICKS_US( 100 ); // Run 10000 times per second
57 | }
58 |
59 | void ADC_Init( void ) {
60 | printf("\n%s called", __FUNCTION__);
61 | Sched_SetWorkfunc(ADC_WORK, ADC_Work);
62 |
63 | // 1MHz adc clock, enabling ch1 and 2
64 | AD0CR = (1 << 21) | (((uint8_t)(PCLKFREQ / 1000000)) << 8) | 0x06;
65 | AD0CR |= (1 << 16); // Burst
66 | AD0DR1;
67 | AD0DR2;
68 | for (int i = 0; i < NUM_ADCH; i++) {
69 | ADres[i] = ADidx[i] = 0;
70 | for (int j = 0; j < NUM_ACCUM; j++) {
71 | ADaccum[i][j] = 0;
72 | }
73 | }
74 | Sched_SetState( ADC_WORK, 2, 0); // Start right away
75 | }
76 |
77 | int32_t ADC_Read(uint32_t chnum) {
78 | int32_t result=-1;
79 | if (chnum >= 1 && chnum <= 2) {
80 | result = ADres[chnum - 1] >> OVERSAMPLING_BITS;
81 | }
82 | return result;
83 | }
84 |
--------------------------------------------------------------------------------
/src/adc.h:
--------------------------------------------------------------------------------
1 | #ifndef ADC_H_
2 | #define ADC_H_
3 |
4 | void ADC_Init(void);
5 | int32_t ADC_Read(uint32_t chnum);
6 |
7 | #endif /* ADC_H_ */
8 |
--------------------------------------------------------------------------------
/src/buzzer.c:
--------------------------------------------------------------------------------
1 | /*
2 | * buzzer.c - Simple buzzer interface for T-962 reflow controller
3 | *
4 | * Copyright (C) 2011,2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include "buzzer.h"
22 | #include
23 | #include "sched.h"
24 |
25 | static BuzzFreq_t requested_buzz_freq;
26 | static uint8_t requested_buzz_volume;
27 | static int32_t requested_buzz_length;
28 |
29 | /*
30 | * The buzzer is hooked up to PWM5 output, but contains an oscillator of
31 | * its own so volume and freq are ignored for now. :(
32 | */
33 |
34 | static int32_t Buzzer_Work(void) {
35 | if (requested_buzz_freq != BUZZ_NONE) {
36 | FIO0SET = (1 << 21);
37 | requested_buzz_freq = BUZZ_NONE;
38 | } else {
39 | // Don't schedule until next beep is requested
40 | requested_buzz_length = -1;
41 | FIO0CLR = (1 << 21);
42 | }
43 | return requested_buzz_length;
44 | }
45 |
46 | void Buzzer_Init(void) {
47 | printf("\n%s ", __FUNCTION__);
48 |
49 | Sched_SetWorkfunc(BUZZER_WORK, Buzzer_Work);
50 | }
51 |
52 | void Buzzer_Beep(BuzzFreq_t freq, uint8_t volume, int32_t ticks) {
53 | if (ticks > 0 || freq == BUZZ_NONE) {
54 | requested_buzz_freq = freq;
55 | requested_buzz_volume = volume;
56 | requested_buzz_length = ticks;
57 | Sched_SetState(BUZZER_WORK, 2, 0);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/buzzer.h:
--------------------------------------------------------------------------------
1 | #ifndef BUZZER_H_
2 | #define BUZZER_H_
3 |
4 | #include
5 |
6 | typedef enum eBuzzFreq {
7 | BUZZ_NONE = 0,
8 | BUZZ_2KHZ = 3,
9 | BUZZ_1KHZ = 4,
10 | BUZZ_500HZ = 5,
11 | BUZZ_250HZ = 6
12 | } BuzzFreq_t;
13 |
14 | void Buzzer_Init(void);
15 | void Buzzer_Beep(BuzzFreq_t freq, uint8_t volume, int32_t ticks);
16 |
17 | #endif /* BUZZER_H_ */
18 |
--------------------------------------------------------------------------------
/src/circbuffer.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "circbuffer.h"
3 |
4 | /**** Circular Buffer used by UART ****/
5 |
6 | void init_circ_buf(tcirc_buf *cbuf) {
7 | cbuf->head = cbuf->tail = 0;
8 | cbuf->dropped = 0;
9 | }
10 |
11 | void add_to_circ_buf(tcirc_buf *cbuf, char ch, int block) {
12 | // Add char to buffer
13 | unsigned int newhead = cbuf->head;
14 | newhead++;
15 | if (newhead >= CIRCBUFSIZE) {
16 | newhead = 0;
17 | }
18 | while (newhead == cbuf->tail) {
19 | if (!block) {
20 | cbuf->dropped++;
21 | return;
22 | }
23 |
24 | // If blocking, this just keeps looping. Due to interrupt-driven
25 | // system the buffer might eventually have space in it, however
26 | // if this is called when interrupts are disabled it will stall
27 | // the system, so the caller is cautioned not to fsck it up.
28 | }
29 |
30 | cbuf->buf[cbuf->head] = ch;
31 | cbuf->head = newhead;
32 | }
33 |
34 |
35 | char get_from_circ_buf(tcirc_buf *cbuf) {
36 | // Get char from buffer
37 | // Be sure to check first that there is a char in buffer
38 | unsigned int newtail = cbuf->tail;
39 | uint8_t retval = cbuf->buf[newtail];
40 |
41 | if (newtail == cbuf->head) {
42 | return 0xFF;
43 | }
44 |
45 | newtail++;
46 | if (newtail >= CIRCBUFSIZE) {
47 | // Rollover
48 | newtail = 0;
49 | }
50 | cbuf->tail = newtail;
51 |
52 | return retval;
53 | }
54 |
55 |
56 | int circ_buf_has_char(tcirc_buf *cbuf) {
57 | // Return true if buffer empty
58 | unsigned int head = cbuf->head;
59 | return (head != cbuf->tail);
60 | }
61 |
62 | unsigned int circ_buf_count(tcirc_buf *cbuf) {
63 | int count;
64 |
65 | count = cbuf->head;
66 | count -= cbuf->tail;
67 | if (count < 0) {
68 | count += CIRCBUFSIZE;
69 | }
70 | return (unsigned int)count;
71 | }
72 |
--------------------------------------------------------------------------------
/src/circbuffer.h:
--------------------------------------------------------------------------------
1 | #ifndef CIRCBUFFER_H_
2 | #define CIRCBUFFER_H_
3 |
4 | /* Size of buffer.
5 | * The putchar() function used here will block when the buffer is full (buffer
6 | * is drained in interrupt), so if you want to continously write lots of data
7 | * increase the buffer size. Change putchar() to non-blocking and read the
8 | * 'dropped' parameter in the buffer structure to determine how your buffer
9 | * size is doing.
10 | */
11 | #define CIRCBUFSIZE 256
12 |
13 | typedef struct {
14 | volatile unsigned int head;
15 | volatile unsigned int tail;
16 | volatile unsigned int dropped;
17 | char buf[CIRCBUFSIZE];
18 | } tcirc_buf;
19 |
20 | void init_circ_buf(tcirc_buf * cbuf);
21 | void add_to_circ_buf(tcirc_buf *cbuf, char ch, int block);
22 | int circ_buf_has_char(tcirc_buf *cbuf);
23 | char get_from_circ_buf(tcirc_buf *cbuf);
24 | unsigned int circ_buf_count(tcirc_buf *cbuf);
25 |
26 | #endif /* CIRCBUFFER_H_ */
27 |
--------------------------------------------------------------------------------
/src/cr_startup_lpc21.s:
--------------------------------------------------------------------------------
1 | /*****************************************************************************
2 | * +--+
3 | * | ++----+
4 | * +-++ |
5 | * | |
6 | * +-+--+ |
7 | * | +--+--+
8 | * +----+ Copyright (c) 2009-13 Code Red Technologies Ltd.
9 | *
10 | * LPC21xx Startup code for use with Red Suite
11 | *
12 | * Version : 130903
13 | *
14 | * Software License Agreement
15 | *
16 | * The software is owned by Code Red Technologies and/or its suppliers, and is
17 | * protected under applicable copyright laws. All rights are reserved. Any
18 | * use in violation of the foregoing restrictions may subject the user to criminal
19 | * sanctions under applicable laws, as well as to civil liability for the breach
20 | * of the terms and conditions of this license.
21 | *
22 | * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
23 | * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
25 | * USE OF THIS SOFTWARE FOR COMMERCIAL DEVELOPMENT AND/OR EDUCATION IS SUBJECT
26 | * TO A CURRENT END USER LICENSE AGREEMENT (COMMERCIAL OR EDUCATIONAL) WITH
27 | * CODE RED TECHNOLOGIES LTD.
28 | *
29 | ******************************************************************************/
30 |
31 | /*******************************************************************************
32 | *
33 | * Conventions used in the assembler code:
34 | *
35 | * Mnemonics are in upper case.
36 | * labels (prog or data) are in lower case.
37 | * Constants for initialization values and/or conditional assembly are
38 | * in upper case.
39 | *
40 | * The following C preprocessor 'switches' are used in this file to conditionally
41 | * assemble certain parts of the code:
42 | *
43 | * PLL_INIT : Define this to initialise the PLL
44 | * VPB_INIT : Define this to initialise the VPB divider
45 | * MAM_INIT : Define this to initialise the MAM
46 | * STACK_INIT : Define this to initialise the STACK
47 | * USB_INIT : Define this to enable USB (makes 8KB of USB RAM available
48 | * on LPC2146/2148)
49 | * USE_OLD_STYLE_DATA_BSS_INIT : Define this to build startup code with pre-3.6
50 | * version of the Code Red tools
51 | *
52 | *******************************************************************************/
53 |
54 | .global main // int main(void)
55 |
56 | #ifndef USE_OLD_STYLE_DATA_BSS_INIT
57 | /*****************************************************************************
58 | / The following symbols are constructs generated by the linker, indicating
59 | / the location of various points in the "Global Section Table". This table is
60 | / created by the linker via the Code Red managed linker script mechanism. It
61 | / contains the load address, execution address and length of each RW data
62 | / section and the execution and length of each BSS (zero initialized) section.
63 | /*****************************************************************************/
64 | .global __data_section_table
65 | .global __data_section_table_end
66 | .global __bss_section_table
67 | .global __bss_section_table_end
68 | #else
69 | /*****************************************************************************
70 | / The following symbols are constructs generated by the linker, indicating
71 | / the load address, execution address and length of the RW data section and
72 | / the execution and length of the BSS (zero initialized) section.
73 | / Note that these symbols are not normally used by the managed linker script
74 | / mechanism in Red Suite/LPCXpresso 3.6 (Windows) and LPCXpresso 3.8 (Linux).
75 | / They are provide here simply so this startup code can be used with earlier
76 | / versions of Red Suite which do not support the more advanced managed linker
77 | / script mechanism introduced in the above version. To enable their use,
78 | / define "USE_OLD_STYLE_DATA_BSS_INIT".
79 | /*****************************************************************************/
80 | .global _etext
81 | .global _data
82 | .global _edata
83 | .global _bss
84 | .global _ebss;
85 | #endif
86 |
87 | .global _vStackTop // top of stack
88 |
89 | /*
90 | * Stack Sizes - remember these are bytes not words
91 | */
92 | .set UND_STACK_SIZE, 0x00000010
93 | .set ABT_STACK_SIZE, 0x00000010
94 | .set FIQ_STACK_SIZE, 0x00000010
95 | .set IRQ_STACK_SIZE, 0X00000080
96 | .set SVC_STACK_SIZE, 0x00000080
97 |
98 | .set USR_STACK_SIZE, 0x00000400 // Shared with SYS mode
99 |
100 | .set TOTAL_STACK_SIZE, UND_STACK_SIZE + SVC_STACK_SIZE + \
101 | ABT_STACK_SIZE + FIQ_STACK_SIZE + \
102 | IRQ_STACK_SIZE + USR_STACK_SIZE
103 |
104 | /*
105 | * Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
106 | */
107 | .set MODE_USR, 0x10 // User Mode
108 | .set MODE_FIQ, 0x11 // FIQ Mode
109 | .set MODE_IRQ, 0x12 // IRQ Mode
110 | .set MODE_SVC, 0x13 // Supervisor Mode
111 | .set MODE_ABT, 0x17 // Abort Mode
112 | .set MODE_UND, 0x1B // Undefined Mode
113 | .set MODE_SYS, 0x1F // System Mode
114 |
115 | .set I_BIT, 0x80 // when I bit is set, IRQ is disabled
116 | .set F_BIT, 0x40 // when F bit is set, FIQ is disabled
117 |
118 | .text
119 | .code 32
120 | .align 2
121 |
122 | .section .isr_vector,"x"
123 | .global _boot
124 | .func _boot
125 | _boot:
126 |
127 | /*
128 | * Exception Processing Vectors
129 | */
130 | Vectors:
131 | B _start // reset
132 | MOV pc,#__undf // undefined
133 | MOV pc,#__swi // SWI/SVC
134 | MOV pc,#__pabt // program abort
135 | MOV pc,#__dabt // data abort
136 | NOP // Reserved for the flash checksum
137 | LDR pc,[pc,#-0xFF0] // IRQ - read the VIC register
138 | // LDR pc,_irq // or go to default handler
139 | // MOV pc,#__fiq // Do __fiq in-place instead
140 | __fiq: MOV r0,#_fiqstr // FIQ
141 | B _printexc
142 | __undf: MOV r0,#_undfstr // undefined
143 | B _printexc
144 | __pabt: MOV r0,#_pabtstr // program abort
145 | B _printexc
146 | __dabt: MOV r0,#_dabtstr // data abort
147 | // lr contains instruction that failed to access data +8 so adjust an additional 4 bytes
148 | sub lr,lr,#4
149 | B _printexc
150 | __swi:
151 | MOV r0,#_swistr
152 | // B _printexc // Fall-thru
153 | _printexc:
154 | .set UART0, 0xe000c000
155 | .set U0LSR_OFFS, 0x14
156 | .set U0IER_OFFS, 0x04
157 | .set U0THR_OFFS, 0x00
158 | .set FIO0CLR, 0x3fffc01c
159 | // lr contains address of failed instruction +4
160 | SUB lr,lr,#4
161 | LDR r1,=UART0
162 | // MOV r2,#0
163 | // STR r2,[r1,#U0IER_OFFS]
164 |
165 | // Output exception text
166 | _ploop:
167 | LDRB r2,[r0],#1
168 | _pwait:
169 | LDR r3,[r1,#U0LSR_OFFS]
170 | TST r3,#0x20
171 | BEQ _pwait
172 | CMP r2,#0
173 | STRNE r2,[r1,#U0THR_OFFS]
174 | BNE _ploop
175 | _done:
176 |
177 | // Output lr register in hex
178 | MOV r5,#8
179 | MOV r4,#_hexstr
180 | _hloop:
181 | LSR r0,lr,#0x1c
182 | LDR r2,[r4,r0]
183 | _pwait2:
184 | LDR r3,[r1,#U0LSR_OFFS]
185 | TST r3,#0x20
186 | BEQ _pwait2
187 | STR r2,[r1,#U0THR_OFFS]
188 | LSL lr,lr,#4
189 | SUBS r5,r5,#1
190 | BNE _hloop
191 |
192 | // Turn off backlight to indicate fault
193 | // LDR r1,=FIO0CLR
194 | // MOV r2,#(1<<11)
195 | // STR r2,[r1]
196 | _realdone:
197 | B _realdone
198 |
199 | _hexstr: .ASCII "0123456789abcdef"
200 | _undfstr: .ASCIZ "UNDF@"
201 | _pabtstr: .ASCIZ "PABT@"
202 | _dabtstr: .ASCIZ "DABT@"
203 | _fiqstr: .ASCIZ "FIQ?"
204 | _swistr: .ASCIZ "SWI?"
205 |
206 | .endfunc
207 | /*
208 | * Setup the operating mode & stack.
209 | */
210 | .global _start, start, _mainCRTStartup
211 | .func _start
212 |
213 | _start:
214 | start:
215 | _mainCRTStartup:
216 |
217 | .set SYSCTRL, 0xE01FC040
218 |
219 | #ifdef PLL_INIT
220 |
221 | .set PLL, SYSCTRL+0x40
222 | .set PLLCON_OFFSET, 0x0
223 | .set PLLCFG_OFFSET, 0x4
224 | .set PLLSTAT_OFFSET, 0x8
225 | .set PLLFEED_OFFSET, 0xC
226 |
227 | .set PLOCK, (1<<10) //lock bit inside PLLSTAT
228 |
229 | .set SET_PLLCFG_MUL3, 0x2
230 | .set SET_PLLCFG_MUL4, 0x3
231 | .set SET_PLLCFG_MUL5, 0x4
232 | .set SET_PLLCFG_MUL6, 0x5
233 |
234 | .set SET_PLLCFG_DIV1, (0x0<<4)
235 | .set SET_PLLCFG_DIV2, (0x1<<4)
236 | .set SET_PLLCFG_DIV4, (0x2<<4)
237 | .set SET_PLLCFG_DIV8, (0x3<<4)
238 |
239 |
240 | .set PLLCFG_INIT_VAL, SET_PLLCFG_MUL6 | SET_PLLCFG_DIV1
241 | .set SET_PLLCON_ENABLE, 1
242 | .set SET_PLLCON_CONNECT, 2
243 |
244 | /*
245 | * Setup the PLL
246 | */
247 |
248 | LDR R0,=PLL
249 | MOV R1,#0xAA
250 | MOV R2,#0x55
251 |
252 | MOV R3,#PLLCFG_INIT_VAL
253 | STR R3,[R0,#PLLCFG_OFFSET]
254 |
255 | MOV R3,#SET_PLLCON_ENABLE
256 | STR R3,[R0,#PLLCON_OFFSET]
257 |
258 | STR R1,[R0,#PLLFEED_OFFSET]
259 | STR R2,[R0,#PLLFEED_OFFSET]
260 |
261 | // Wait for the loop to lock
262 | 1: LDR R3,[R0,#PLLSTAT_OFFSET]
263 | ANDS R3,R3,#PLOCK
264 | BEQ 1b
265 |
266 | // Now swap the cpu clock to the PLL
267 | MOV R3,#(SET_PLLCON_ENABLE | SET_PLLCON_CONNECT)
268 | STR R3,[R0,#PLLCON_OFFSET]
269 | STR R1,[R0,#PLLFEED_OFFSET]
270 | STR R2,[R0,#PLLFEED_OFFSET]
271 |
272 | #endif
273 |
274 |
275 |
276 | #ifdef VPB_INIT
277 | /*
278 | * Setup the VPB/APB Peripheral bus clock
279 | */
280 |
281 | .set VPBDIV_OFFSET, 0xc0
282 | .set VPBDIV, SYSCTRL+VPBDIV_OFFSET
283 |
284 | .set VPBDIV_INIT_VAL, 1
285 |
286 | LDR R0,=VPBDIV
287 | LDR R1,=VPBDIV_INIT_VAL
288 | STR R1,[R0]
289 |
290 | #endif
291 |
292 |
293 | #ifdef MAM_INIT
294 | /*
295 | * Setup the Memory Accelerator Block (MAM)
296 | */
297 |
298 | .set MAM, 0xE01FC000
299 | .set MAMCR_OFFSET, 0x0
300 | .set MAMTIM_OFFSET, 0x4
301 |
302 | .set SET_MAMCR_DISABLE, 0x0
303 | .set SET_MAMCR_PARTIAL, 0x1
304 | .set SET_MAMCR_FULL, 0x2
305 |
306 | // How many cycles for flash access
307 | .set SET_MAMTIM_0CLK, 0x0
308 | .set SET_MAMTIM_1CLK, 0x1
309 | .set SET_MAMTIM_2CLK, 0x2
310 | .set SET_MAMTIM_3CLK, 0x3
311 | .set SET_MAMTIM_4CLK, 0x4
312 | .set SET_MAMTIM_5CLK, 0x5
313 | .set SET_MAMTIM_6CLK, 0x6
314 | .set SET_MAMTIM_7CLK, 0x7
315 |
316 |
317 | .set MAMTIM_INIT_VAL, SET_MAMTIM_3CLK
318 | .set MAMCR_INIT_VAL, SET_MAMCR_FULL
319 |
320 | LDR R0,=MAM
321 | MOV R1,#MAMTIM_INIT_VAL
322 | STR R1,[R0,#MAMTIM_OFFSET]
323 | MOV R1,#MAMCR_INIT_VAL
324 | STR R1,[R0,#MAMCR_OFFSET]
325 |
326 | #endif
327 |
328 | /***************************
329 | * The LPC2146/2148 have 8KB of USB RAM available. If USB is
330 | * not being used, then this RAM can be used as general
331 | * purpose RAM by the application. However this requires the
332 | * USB subsystem to be enabled in the PCONP register
333 | ***************************/
334 | #ifdef USB_INIT
335 | LDR r0, =0xE01FC0C4 // Load address of PCONP register
336 | LDR r1,[r0] // Load contents of PCONP
337 | ORR r1,r1,#(1 << 31) // Set bit 31 - USB
338 | STR r1,[r0] // Store updated value back to PCONP
339 | #endif
340 |
341 | /*
342 | * Setup some stack space for each ARM operating mode
343 | */
344 | LDR r0,=_vStackTop
345 | MSR CPSR_c,#MODE_UND|I_BIT|F_BIT // Undefined Instruction Mode
346 | MOV sp,r0
347 | SUB r0,r0,#UND_STACK_SIZE
348 | MSR CPSR_c,#MODE_ABT|I_BIT|F_BIT // Abort Mode
349 | MOV sp,r0
350 | SUB r0,r0,#ABT_STACK_SIZE
351 | MSR CPSR_c,#MODE_FIQ|I_BIT|F_BIT // FIQ Mode
352 | MOV sp,r0
353 | SUB r0,r0,#FIQ_STACK_SIZE
354 | MSR CPSR_c,#MODE_IRQ|I_BIT|F_BIT // IRQ Mode
355 | MOV sp,r0
356 | SUB r0,r0,#IRQ_STACK_SIZE
357 | MSR CPSR_c,#MODE_SVC|I_BIT|F_BIT // Supervisor Mode
358 | MOV sp,r0
359 | SUB r0,r0,#SVC_STACK_SIZE
360 | MSR CPSR_c,#MODE_SYS|I_BIT|F_BIT // System Mode
361 | MOV sp,r0
362 |
363 | #ifndef USE_OLD_STYLE_DATA_BSS_INIT
364 | /*
365 | * Copy RWdata initial values from flash to its execution
366 | * address in RAM
367 | */
368 | LDR r4, =Ldata_start // Load base address of data...
369 | LDR r4, [r4] // ...from Global Section Table
370 | LDR r5, =Ldata_end // Load end address of data...
371 | LDR r5, [r5] //...from Global Section Table
372 | start_data_init_loop:
373 | CMP r4,r5 // Check to see if reached end of...
374 | BEQ end_data_init_loop // ...data entries in G.S.T.
375 | LDR r0, [r4],#4 // Load LoadAddr from G.S.T.
376 | LDR r1, [r4],#4 // Load ExeAddr from G.S.T.
377 | LDR r2, [r4],#4 // Load SectionLen from G.S.T.
378 | BL data_init // Call subroutine to do copy
379 | B start_data_init_loop // Loop back for next entry in G.S.T.
380 |
381 | end_data_init_loop:
382 | /*
383 | * Clear .bss (zero'ed space)
384 | */
385 | LDR r5, =Lbss_end // Load end address of BSS...
386 | LDR r5, [r5] //...from Global Section Table
387 | start_bss_init_loop:
388 | CMP r4,r5 // Check to see if reached end of...
389 | BEQ post_data_bss_init // ...bss entries in G.S.T.
390 | LDR r0, [r4],#4 // Load ExeAddr from G.S.T.
391 | LDR r1, [r4],#4 // Load SectionLen from G.S.T.
392 | BL bss_init // Call subroutine to do zero'ing
393 | B start_bss_init_loop // Loop back for next entry in G.S.T.
394 |
395 | Ldata_start: .word __data_section_table
396 | Ldata_end: .word __data_section_table_end
397 | Lbss_end: .word __bss_section_table_end
398 |
399 | /******************************************************************************
400 | * Functions to carry out the initialization of RW and BSS data sections. These
401 | * are written as separate functions to cope with MCUs with multiple banks of
402 | * memory.
403 | ******************************************************************************/
404 | // void data_init(unsigned int romstart, unsigned int start, unsigned int len)
405 | data_init:
406 | MOV r12,#0
407 | .di_loop:
408 | CMP r12,r2
409 | LDRLO r3,[r0],#4
410 | STRLO r3,[r1],#4
411 | ADDLO r12,r12,#4
412 | BLO .di_loop
413 | BX LR
414 |
415 | // void bss_init(unsigned int start, unsigned int len)
416 | bss_init:
417 | MOV r12,#0
418 | MOV r2, #0
419 | .bi_loop:
420 | CMP r12,r1
421 | STRLO r2,[r0],#4
422 | ADDLO r12,r12,#4
423 | BLO .bi_loop
424 | BX LR
425 |
426 |
427 | /******************************************************************************
428 | * Back to main flow of Reset_Handler
429 | ******************************************************************************/
430 | post_data_bss_init:
431 |
432 | #else
433 | // Use Old Style Data and BSS section initialization.
434 | // This will only initialize a single RAM bank
435 | //
436 | // Copy initialized data to its execution address in RAM
437 | LDR r1,=_etext // -> ROM data start
438 | LDR r2,=_data // -> data start
439 | LDR r3,=_edata // -> end of data
440 | 1: CMP r2,r3 // check if data to move
441 | LDRLO r0,[r1],#4 // copy it
442 | STRLO r0,[r2],#4
443 | BLO 1b // loop until done
444 |
445 | //Clear .bss (zero'ed space)
446 | MOV r0,#0 // get a zero
447 | LDR r1,=_bss // -> bss start
448 | LDR r2,=_ebss // -> bss end
449 | 2: CMP r1,r2 // check if data to clear
450 | STRLO r0,[r1],#4 // clear 4 bytes
451 | BLO 2b // loop until done
452 | #endif
453 |
454 | #ifdef STACK_INIT
455 | /*
456 | * Initialize the stack to known values to aid debugging
457 | *
458 | * Definitely optional, but can help early debugging for
459 | * stack overflows. Also system optimization for measuring
460 | * stack depth used.
461 | */
462 | .global _vStackTop
463 |
464 | MOV r0,#0 // start of count
465 | LDR r1,=_vStackTop // start from top
466 | LDR r3,=TOTAL_STACK_SIZE
467 | SUB r2,r1,r3
468 | 3:
469 | CMP r1,r2
470 | STRGT r0,[r1,#-4]! // walk down the stack
471 | ADD r0,r0,#1
472 | BGT 3b
473 | #endif
474 |
475 | //
476 | // Call C++ library initilisation, if present
477 | //
478 | .cpp_init:
479 | LDR r3, .def__libc_init_array // if
480 | CMP r3, #0
481 | BEQ .skip_cpp_init
482 | BL __libc_init_array
483 | .skip_cpp_init:
484 |
485 | /*
486 | * Call main program: main(0)
487 | */
488 | MOV r0,#0 // no arguments (argc = 0)
489 | // MOV r1,r0
490 | // MOV r2,r0
491 | MOV fp,r0 // null frame pointer
492 | // MOV r7,r0 // null frame pointer for thumb
493 |
494 | // Change to system mode (IRQs enabled) before calling main application
495 |
496 | MSR CPSR_c,#MODE_SYS|F_BIT // System Mode
497 |
498 | #ifdef __REDLIB__
499 | LDR r10, =__main
500 | #else
501 | LDR r10, =main
502 | #endif
503 |
504 | MOV lr,pc
505 | BX r10 // enter main() - could be ARM or Thumb
506 |
507 | .size _start, . - _start
508 | .endfunc
509 |
510 | .global _reset, reset
511 | .func _reset
512 | _reset:
513 | reset:
514 | exit:
515 |
516 | B . // loop until reset
517 |
518 | .weak __libc_init_array
519 | .def__libc_init_array:
520 | .word __libc_init_array
521 |
522 |
523 | .weak init_lpc3xxx // void init_lpc31xx(void)
524 | .def__init_lpc3xxx:
525 | .word init_lpc3xxx
526 |
527 |
528 | .size _reset, . - _reset
529 | .endfunc
530 |
531 | .end
532 |
--------------------------------------------------------------------------------
/src/crp.c:
--------------------------------------------------------------------------------
1 | //*****************************************************************************
2 | // crp.c
3 | //
4 | // Source file to create CRP word expected by LPCXpresso IDE linker
5 | //*****************************************************************************
6 | //
7 | // Copyright(C) NXP Semiconductors, 2013
8 | // All rights reserved.
9 | //
10 | // Software that is described herein is for illustrative purposes only
11 | // which provides customers with programming information regarding the
12 | // LPC products. This software is supplied "AS IS" without any warranties of
13 | // any kind, and NXP Semiconductors and its licensor disclaim any and
14 | // all warranties, express or implied, including all implied warranties of
15 | // merchantability, fitness for a particular purpose and non-infringement of
16 | // intellectual property rights. NXP Semiconductors assumes no responsibility
17 | // or liability for the use of the software, conveys no license or rights under any
18 | // patent, copyright, mask work right, or any other intellectual property rights in
19 | // or to any products. NXP Semiconductors reserves the right to make changes
20 | // in the software without notification. NXP Semiconductors also makes no
21 | // representation or warranty that such application will be suitable for the
22 | // specified use without further testing or modification.
23 | //
24 | // Permission to use, copy, modify, and distribute this software and its
25 | // documentation is hereby granted, under NXP Semiconductors' and its
26 | // licensor's relevant copyrights in the software, without fee, provided that it
27 | // is used in conjunction with NXP Semiconductors microcontrollers. This
28 | // copyright, permission, and disclaimer notice must appear in all copies of
29 | // this code.
30 | //*****************************************************************************
31 |
32 | #if defined (__CODE_RED)
33 | #include
34 | // Variable to store CRP value in. Will be placed automatically
35 | // by the linker when "Enable Code Read Protect" selected.
36 | // See crp.h header for more information
37 | __CRP const unsigned int CRP_WORD = CRP_NO_CRP ;
38 | #endif
39 |
--------------------------------------------------------------------------------
/src/eeprom.c:
--------------------------------------------------------------------------------
1 | /*
2 | * eeprom.c - I2C EEPROM interface for T-962 reflow controller
3 | *
4 | * Copyright (C) 2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include
22 | #include
23 | #include
24 | #include "t962.h"
25 | #include "eeprom.h"
26 | #include "i2c.h"
27 |
28 | #define EEADDR (0x50<<1)
29 | //#define DUMP_EEPROM
30 |
31 | void EEPROM_Init(void) {
32 | #ifdef DUMP_EEPROM
33 | EEPROM_Dump();
34 | #endif
35 | // No init needed at this point, maybe detect the actual presence some day
36 | }
37 |
38 | void EEPROM_Dump(void) {
39 | uint8_t dumpbuf[256];
40 | EEPROM_Read(dumpbuf, 0, sizeof(dumpbuf));
41 | printf("\nEEPROM contents:");
42 | for (int i = 0; i < sizeof(dumpbuf); i++) {
43 | if ((i & 0x0f) == 0){
44 | printf("\n0x%04x:", i);
45 | }
46 | printf(" %02x", dumpbuf[i]);
47 | }
48 | }
49 |
50 | int32_t EEPROM_Read(uint8_t* dest, uint32_t startpos, uint32_t len) {
51 | int32_t retval = 0;
52 | if (startpos < 256 && dest && len && len <= 256) {
53 | uint8_t offset = (uint8_t)startpos;
54 | retval = I2C_Xfer(EEADDR, &offset, 1, 0); // Set address pointer to startpos
55 | if (!retval) {
56 | retval = I2C_Xfer(EEADDR | 1, dest, len, 1); // Read requested data
57 | }
58 | }
59 | return retval;
60 | }
61 |
62 | int32_t EEPROM_Write(uint32_t startdestpos, uint8_t* src, uint32_t len) {
63 | int32_t retval = 0;
64 | if (startdestpos < 256 && len && len <= 256) {
65 | uint8_t tmpbuf[9];
66 | uint8_t i = startdestpos;
67 | while (len) {
68 | uint32_t loopcnt = 0;
69 | uint8_t startoffset = i & 0x07;
70 | uint8_t maxcopysize = 8 - startoffset;
71 | // up to 8 bytes at a time depending on alignment
72 | uint8_t bytestocopy = (len > maxcopysize) ? maxcopysize : len;
73 | tmpbuf[0] = i;
74 | memcpy(tmpbuf + 1, src, bytestocopy);
75 | // Set address pointer and provide up to 8 bytes of data for page write
76 | retval = I2C_Xfer(EEADDR, tmpbuf, bytestocopy + 1, 1);
77 | if (!retval) {
78 | do {
79 | // Dummy write to poll timed write cycle completion
80 | retval = I2C_Xfer(EEADDR, tmpbuf, 1, 1);
81 | loopcnt++;
82 | // 5ms max write cycle. 200kHz bus freq & 10 bits per poll makes this a 20ms timeout
83 | } while (retval && loopcnt < 400);
84 | if (retval) {
85 | printf("\nTimeout getting ACK from EEPROM during write!");
86 | break;
87 | }
88 | len -= bytestocopy;
89 | i += bytestocopy;
90 | src += bytestocopy;
91 | } else {
92 | printf("\nFailed to write to EEPROM!");
93 | retval = -2;
94 | break;
95 | }
96 | }
97 | } else {
98 | printf("\nInvalid EEPROM addressing");
99 | retval = -3;
100 | }
101 | return retval;
102 | }
103 |
--------------------------------------------------------------------------------
/src/eeprom.h:
--------------------------------------------------------------------------------
1 | #ifndef EEPROM_H_
2 | #define EEPROM_H_
3 |
4 | void EEPROM_Init(void);
5 | void EEPROM_Dump(void);
6 | int32_t EEPROM_Read(uint8_t* dest, uint32_t startpos, uint32_t len);
7 | int32_t EEPROM_Write(uint32_t startdestpos, uint8_t* src, uint32_t len);
8 |
9 | #endif /* EEPROM_H_ */
10 |
--------------------------------------------------------------------------------
/src/i2c.c:
--------------------------------------------------------------------------------
1 | /*
2 | * i2c.c - I2C interface for T-962 reflow controller
3 | *
4 | * Copyright (C) 2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include
22 | #include
23 | #include "t962.h"
24 | #include "i2c.h"
25 |
26 | // Limit to i2c speed 200kHz because of the relatively weak 4k7 pullups
27 | #define I2CSPEED (200000)
28 |
29 | void I2C_Init(void) {
30 | uint8_t dummybyte;
31 | I20SCLL = I20SCLH = PCLKFREQ / I2CSPEED / 2;
32 | I20CONCLR = 0xff;
33 | I20CONSET = (1 << 6); // I2EN
34 | I2C_Xfer(0xff, &dummybyte, 0, 1); // Dummy initial xfer
35 | }
36 |
37 | #define I2CSTART (0x08)
38 | #define I2CRSTART (0x10)
39 | #define I2CWAACK (0x18)
40 | #define I2CWANOACK (0x20)
41 | #define I2CWDACK (0x28)
42 | #define I2CWDNOACK (0x30)
43 | #define I2CARBLOST (0x38)
44 | #define I2CRAACK (0x40)
45 | #define I2CRANOACK (0x48)
46 | #define I2CRDACK (0x50)
47 | #define I2CRDNOACK (0x58)
48 |
49 | int32_t I2C_Xfer(uint8_t slaveaddr, uint8_t* theBuffer, uint32_t theLength, uint8_t trailingStop) {
50 | int32_t retval = 0;
51 | int done = 0;
52 | uint8_t stat;
53 |
54 | I20CONSET = (1 << 5); // STA
55 | //printf("\n[STA]");
56 |
57 | while (!done) {
58 | while (!(I20CONSET & (1 << 3))); // SI
59 | stat = I20STAT;
60 | //printf("[0x%02x]", stat);
61 | switch(stat) {
62 | case I2CSTART:
63 | case I2CRSTART:
64 | I20DAT = slaveaddr;
65 | I20CONCLR = (1 << 5); // Clear STA
66 | //printf("[WADDR]");
67 | break;
68 | case I2CWANOACK:
69 | case I2CWDNOACK:
70 | case I2CARBLOST:
71 | case I2CRANOACK:
72 | //printf("[I2C error!]");
73 | trailingStop = 1; // Force STOP condition at the end no matter what
74 | retval = -1;
75 | done = 1;
76 | break;
77 |
78 | case I2CWAACK:
79 | case I2CWDACK:
80 | if (theLength) {
81 | I20DAT = *theBuffer++;
82 | theLength--;
83 | //printf("[WDATA]");
84 | } else {
85 | done=1;
86 | }
87 | break;
88 |
89 | case I2CRAACK:
90 | //printf("[RADDR]");
91 | I20CONSET = (1 << 2); // Set AA
92 | break;
93 |
94 | case I2CRDACK:
95 | case I2CRDNOACK:
96 | *theBuffer++ = I20DAT;
97 | theLength--;
98 | if (theLength == 1) {
99 | I20CONCLR = (1 << 2); // Clear AA for last byte
100 | } else if (theLength == 0) {
101 | done = 1;
102 | }
103 | //if(!done) printf("[RDATA]");
104 | break;
105 | }
106 | I20CONCLR = (1 << 3); // Clear SI
107 | }
108 |
109 | if (trailingStop) {
110 | I20CONSET = (1 << 4); // STO
111 | //printf("[STO]");
112 | while (I20CONSET & (1 << 4)); // Wait for STO to clear
113 | }
114 | return retval;
115 | }
116 |
--------------------------------------------------------------------------------
/src/i2c.h:
--------------------------------------------------------------------------------
1 | #ifndef I2C_H_
2 | #define I2C_H_
3 |
4 | void I2C_Init(void);
5 | int32_t I2C_Xfer(uint8_t slaveaddr, uint8_t* theBuffer, uint32_t theLength, uint8_t trailingStop);
6 |
7 | #endif /* I2C_H_ */
8 |
--------------------------------------------------------------------------------
/src/images/UEoSlogo-128x64.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/src/images/UEoSlogo-128x64.bmp
--------------------------------------------------------------------------------
/src/images/editprofile-18x64.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/src/images/editprofile-18x64.bmp
--------------------------------------------------------------------------------
/src/images/f3edit-18x16.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/src/images/f3edit-18x16.bmp
--------------------------------------------------------------------------------
/src/images/graph-128x64.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/src/images/graph-128x64.bmp
--------------------------------------------------------------------------------
/src/images/selectprofile-18x64.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/src/images/selectprofile-18x64.bmp
--------------------------------------------------------------------------------
/src/images/stop-18x64.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnifiedEngineering/T-962-improvements/217281cccefd046c5547b69e21bc9e6670679fbd/src/images/stop-18x64.bmp
--------------------------------------------------------------------------------
/src/import.s:
--------------------------------------------------------------------------------
1 | .section ".text"
2 | .global logobmp
3 | .global logobmpsize
4 | logobmp:
5 | .incbin "../src/images/UEoSlogo-128x64.bmp"
6 | logobmpsize:
7 | .word .-logobmp
8 |
9 | .global graphbmp
10 | .global graphbmpsize
11 | graphbmp:
12 | .incbin "../src/images/graph-128x64.bmp"
13 | graphbmpsize:
14 | .word .-graphbmp
15 |
16 | .global stopbmp
17 | .global stopbmpsize
18 | stopbmp:
19 | .incbin "../src/images/stop-18x64.bmp"
20 | stopbmpsize:
21 | .word .-stopbmp
22 |
23 | .global selectbmp
24 | .global selectbmpsize
25 | selectbmp:
26 | .incbin "../src/images/selectprofile-18x64.bmp"
27 | selectbmpsize:
28 | .word .-selectbmp
29 |
30 | .global editbmp
31 | .global editbmpsize
32 | editbmp:
33 | .incbin "../src/images/editprofile-18x64.bmp"
34 | editbmpsize:
35 | .word .-editbmp
36 |
37 | .global f3editbmp
38 | .global f3editbmpsize
39 | f3editbmp:
40 | .incbin "../src/images/f3edit-18x16.bmp"
41 | f3editbmpsize:
42 | .word .-f3editbmp
43 |
--------------------------------------------------------------------------------
/src/io.c:
--------------------------------------------------------------------------------
1 | /*
2 | * io.c - I/O handling for T-962 reflow controller
3 | *
4 | * Copyright (C) 2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include
22 | #include
23 | #include "t962.h"
24 | #include "io.h"
25 | #include "sched.h"
26 | #include "vic.h"
27 |
28 | void Set_Heater(uint8_t enable) {
29 | if (enable < 0xff) {
30 | PINSEL0 |= (2<<18); // Make sure PWM6 function is enabled
31 | } else { // Fully on is dealt with separately to avoid output glitch
32 | PINSEL0 &= ~(2<<18); // Disable PWM6 function on pin
33 | enable = 0xfe; // Not fully on according to PWM hardware but we force GPIO low anyway
34 | }
35 | PWMMR6 = 0xff - enable;
36 | PWMLER |= (1<<6);
37 | }
38 |
39 | void Set_Fan(uint8_t enable) {
40 | if (enable < 0xff) {
41 | PINSEL0 |= (2<<16); // Make sure PWM4 function is enabled
42 | } else { // Fully on is dealt with separately to avoid output glitch
43 | PINSEL0 &= ~(2<<16); // Disable PWM4 function on pin
44 | enable = 0xfe; // Not fully on according to PWM hardware but we force GPIO low anyway
45 | }
46 | PWMMR4 = 0xff - enable;
47 | PWMLER |= (1<<4);
48 | }
49 |
50 | static int32_t Sleep_Work(void) {
51 | // FIO0PIN ^= (1<<31); // Toggle debug LED
52 | // For some reason P0.31 status cannot be read out, so the following is used instead:
53 | static uint8_t flip = 0;
54 | if (flip) {
55 | FIO0SET = (1<<31);
56 | } else {
57 | FIO0CLR = (1<<31);
58 | }
59 | flip ^= 1;
60 |
61 | // If interrupts are used they must be disabled around the following two instructions!
62 | uint32_t save = VIC_DisableIRQ();
63 | WDFEED = 0xaa; // Feed watchdog
64 | WDFEED = 0x55;
65 | VIC_RestoreIRQ(save);
66 | return TICKS_SECS(1);
67 | }
68 |
69 | void IO_InitWatchdog(void) {
70 | // Setup watchdog
71 | // Some margin (PCLKFREQ/4 would be exactly the period the WD is fed by sleep_work)
72 | WDTC = PCLKFREQ / 3;
73 | WDMOD = 0x03; // Enable
74 | WDFEED = 0xaa;
75 | WDFEED = 0x55;
76 | }
77 |
78 | void IO_PrintResetReason(void) {
79 | uint8_t resetreason = RSIR;
80 | RSIR = 0x0f; // Clear it out
81 | printf(
82 | "\nReset reason(s): %s%s%s%s",
83 | (resetreason & (1 << 0)) ? "[POR]" : "",
84 | (resetreason & (1 << 1)) ? "[EXTR]" : "",
85 | (resetreason & (1 << 2)) ? "[WDTR]" : "",
86 | (resetreason & (1 << 3)) ? "[BODR]" : "");
87 | }
88 |
89 |
90 | // Support for boot ROM functions (get part number etc)
91 | typedef void (*IAP)(unsigned int [], unsigned int[]);
92 | static IAP iap_entry = (void*)0x7ffffff1;
93 |
94 | static partmapStruct partmap[] = {
95 | {"LPC2131(/01)", 0x0002ff01}, // Probably pointless but present for completeness (32kB flash is too small for factory image)
96 | {"LPC2132(/01)", 0x0002ff11},
97 | {"LPC2134(/01)", 0x0002ff12},
98 | {"LPC2136(/01)", 0x0002ff23},
99 | {"LPC2138(/01)", 0x0002ff25},
100 |
101 | {"LPC2141", 0x0402ff01}, // Probably pointless but present for completeness (32kB flash is too small for factory image)
102 | {"LPC2142", 0x0402ff11},
103 | {"LPC2144", 0x0402ff12},
104 | {"LPC2146", 0x0402ff23},
105 | {"LPC2148", 0x0402ff25},
106 | };
107 | #define NUM_PARTS (sizeof(partmap) / sizeof(partmap[0]))
108 |
109 | static uint32_t command[1];
110 | static uint32_t result[3];
111 |
112 | int IO_Partinfo(char* buf, int n, char* format) {
113 | uint32_t partrev;
114 |
115 | // Request part number
116 | command[0] = IAP_READ_PART;
117 | iap_entry((void *)command, (void *)result);
118 | const char* partstrptr = NULL;
119 | for (int i = 0; i < NUM_PARTS; i++) {
120 | if (result[1] == partmap[i].id) {
121 | partstrptr = partmap[i].name;
122 | break;
123 | }
124 | }
125 |
126 | // Read part revision
127 | partrev = *(uint8_t*)PART_REV_ADDR;
128 | if (partrev == 0 || partrev > 0x1a) {
129 | partrev = '-';
130 | } else {
131 | partrev += 'A' - 1;
132 | }
133 | return snprintf(buf, n, format, partstrptr, (int)partrev);
134 | }
135 |
136 | void IO_JumpBootloader(void) {
137 | /* Hold F1-Key at boot to force ISP mode */
138 | if ((IOPIN0 & (1 << 23)) == 0) {
139 | // NB: If you want to call this later need to set a bunch of registers back
140 | // to reset state. Haven't fully figured this out yet, might want to
141 | // progmatically call bootloader, not sure. If calling later be sure
142 | // to crank up watchdog time-out, as it's impossible to disable
143 | //
144 | // Bootloader must use legacy mode IO if you call this later too, so do:
145 | // SCS = 0;
146 |
147 | // Turn off FAN & Heater using legacy registers so they stay off during bootloader
148 | // Fan = PIN0.8
149 | // Heater = PIN0.9
150 | IODIR0 = (1 << 8) | (1 << 9);
151 | IOSET0 = (1 << 8) | (1 << 9);
152 |
153 | //Re-enter ISP Mode, this function will never return
154 | command[0] = IAP_REINVOKE_ISP;
155 | iap_entry((void *)command, (void *)result);
156 | }
157 | }
158 |
159 | void IO_Init(void) {
160 | SCS = 0b11; // Enable fast GPIO on both port 0 and 1
161 |
162 | PINSEL0 = 0b10100000000001010101; // PWM6 + PWM4 + I2C0 + UART0
163 | PINSEL1 = 0b00000101000000000000000000000000; // ADC0 1+2
164 |
165 | FIO0MASK = 0b01001101000000100000010001100000; // Mask out all unknown/unused pins
166 | FIO1MASK = 0b11111111000000001111111111111111; // Only LCD D0-D7
167 |
168 | FIO0DIR = 0b10000010011011000011101100000001; // Default output pins
169 | FIO1DIR = 0b00000000000000000000000000000000;
170 |
171 | FIO0PIN = 0x00; // Turn LED on and make PWM outputs active when in GPIO mode (to help 100% duty cycle issue)
172 |
173 | PWMPR = PCLKFREQ / (256 * 5); // Let's have the PWM perform 5 cycles per second with 8 bits of precision (way overkill)
174 | PWMMCR = (1<<1); // Reset TC on mr0 overflow (period time)
175 | PWMMR0 = 0xff; // Period time
176 | PWMLER = (1<<0); // Enable latch on mr0 (Do I really need to do this?)
177 | PWMPCR = (1<<12) | (1<<14); // Enable PWM4 and 6
178 | PWMTCR = (1<<3) | (1<<0); // Enable timer in PWM mode
179 |
180 | Sched_SetWorkfunc(SLEEP_WORK, Sleep_Work);
181 | Sched_SetState(SLEEP_WORK, 2, 0);
182 | }
183 |
--------------------------------------------------------------------------------
/src/io.h:
--------------------------------------------------------------------------------
1 | #ifndef IO_H_
2 | #define IO_H_
3 |
4 | #define IAP_READ_PART (54)
5 | #define IAP_REINVOKE_ISP (57)
6 | #define PART_REV_ADDR (0x0007D070)
7 |
8 | typedef struct {
9 | const char* name;
10 | const uint32_t id;
11 | } partmapStruct;
12 |
13 | void Set_Heater(uint8_t enable);
14 | void Set_Fan(uint8_t enable);
15 | void IO_InitWatchdog(void);
16 | void IO_PrintResetReason(void);
17 | int IO_Partinfo(char* buf, int n, char* format);
18 | void IO_JumpBootloader(void);
19 | void IO_Init(void);
20 | #endif /* IO_H_ */
21 |
--------------------------------------------------------------------------------
/src/keypad.c:
--------------------------------------------------------------------------------
1 | /*
2 | * keypad.c - Keypad interface for T-962 reflow controller
3 | *
4 | * Copyright (C) 2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include
22 | #include
23 | #include "t962.h"
24 | #include "keypad.h"
25 | #include "io.h"
26 | #include "sched.h"
27 |
28 | #define F1KEY_PORTBIT (1 << 23)
29 | #define F2KEY_PORTBIT (1 << 15)
30 | #define F3KEY_PORTBIT (1 << 16)
31 | #define F4KEY_PORTBIT (1 << 4)
32 | #define S_KEY_PORTBIT (1 << 20)
33 |
34 | #define KEYREPEATDELAY (6)
35 |
36 | static uint32_t latchedkeypadstate = 0;
37 |
38 | static uint32_t Keypad_GetRaw(void) {
39 | return ~FIO0PIN & (F1KEY_PORTBIT | F2KEY_PORTBIT | F3KEY_PORTBIT | F4KEY_PORTBIT | S_KEY_PORTBIT);
40 | }
41 |
42 | static int32_t Keypad_Work(void) {
43 | static uint32_t laststate = 0;
44 | static uint16_t laststateunchangedctr = 0;
45 | uint32_t keypadstate = 0;
46 | uint32_t inverted = Keypad_GetRaw();
47 | uint32_t changed = inverted ^ laststate;
48 |
49 | // At this point we only care about when button is pressed, not released
50 | changed &= inverted;
51 |
52 | if (laststate != inverted) {
53 | laststate = inverted;
54 | laststateunchangedctr = 0;
55 | } else {
56 | laststateunchangedctr++;
57 | if (laststateunchangedctr > KEYREPEATDELAY) {
58 | changed = laststate; // Feed key repeat
59 | // For accelerating key repeats
60 | keypadstate |= ((laststateunchangedctr - KEYREPEATDELAY) << 16);
61 | }
62 | }
63 |
64 | if (changed) {
65 | if (changed & F1KEY_PORTBIT) keypadstate |= KEY_F1;
66 | if (changed & F2KEY_PORTBIT) keypadstate |= KEY_F2;
67 | if (changed & F3KEY_PORTBIT) keypadstate |= KEY_F3;
68 | if (changed & F4KEY_PORTBIT) keypadstate |= KEY_F4;
69 | if (changed & S_KEY_PORTBIT) keypadstate |= KEY_S;
70 | }
71 |
72 | latchedkeypadstate &= 0xffff;
73 | latchedkeypadstate |= keypadstate; // Make sure software actually sees the transitions
74 |
75 | if (keypadstate & 0xff) {
76 | //printf("[KEYPAD %02x]",keypadstate & 0xff);
77 | Sched_SetState(MAIN_WORK, 2, 0); // Wake up main task to update UI
78 | }
79 |
80 | return TICKS_MS(100);
81 | }
82 |
83 | uint32_t Keypad_Get(void) {
84 | uint32_t retval = latchedkeypadstate;
85 | latchedkeypadstate = 0;
86 | return retval;
87 | }
88 |
89 | void Keypad_Init(void) {
90 | Sched_SetWorkfunc(KEYPAD_WORK, Keypad_Work);
91 | printf("\nWaiting for keys to be released... ");
92 | // Note that if this takes longer than ~1 second the watchdog will bite
93 | while (Keypad_GetRaw());
94 | printf("Done!");
95 |
96 | // Potential noise gets suppressed as well
97 | Sched_SetState(KEYPAD_WORK, 1, TICKS_MS(250)); // Wait 250ms before starting to scan the keypad
98 | }
99 |
100 |
--------------------------------------------------------------------------------
/src/keypad.h:
--------------------------------------------------------------------------------
1 | #ifndef KEYPAD_H_
2 | #define KEYPAD_H_
3 |
4 | #define KEY_F1 (1<<0)
5 | #define KEY_F2 (1<<1)
6 | #define KEY_F3 (1<<2)
7 | #define KEY_F4 (1<<3)
8 | #define KEY_S (1<<4)
9 | #define KEY_ANY (KEY_F1 | KEY_F2 | KEY_F3 | KEY_F4 | KEY_S)
10 |
11 | uint32_t Keypad_Get(void);
12 | void Keypad_Init(void);
13 |
14 | #endif /* KEYPAD_H_ */
15 |
--------------------------------------------------------------------------------
/src/lcd.c:
--------------------------------------------------------------------------------
1 | /*
2 | * lcd.c - Display handling (KS0108 compatible, with two chip selects, active high)
3 | * for T-962 reflow controller
4 | *
5 | * Copyright (C) 2010,2012,2013,2014 Werner Johansson, wj@unifiedengineering.se
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 |
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 |
17 | * You should have received a copy of the GNU General Public License
18 | * along with this program. If not, see .
19 | */
20 |
21 | #include "LPC214x.h"
22 | #include
23 | #include
24 | #include
25 | #include "lcd.h"
26 | #include "smallfont.h"
27 |
28 | // Frame buffer storage (each "page" is 8 pixels high)
29 | static uint8_t FB[FB_HEIGHT / 8][FB_WIDTH];
30 |
31 | typedef struct __attribute__ ((packed)) {
32 | uint8_t bfType[2]; // 'BM' only in this case
33 | uint32_t bfSize; // Total size
34 | uint16_t bfReserved[2];
35 | uint32_t bfOffBits; // Pixel start byte
36 | uint32_t biSize; // 40 bytes for BITMAPINFOHEADER
37 | int32_t biWidth; // Image width in pixels
38 | int32_t biHeight; // Image height in pixels (if negative image is right-side-up)
39 | uint16_t biPlanes; // Must be 1
40 | uint16_t biBitCount; // Must be 1
41 | uint32_t biCompression; // Only 0 (uncompressed) supported at the moment
42 | uint32_t biSizeImage; // Pixel data size in bytes
43 | int32_t biXPelsPerMeter;
44 | int32_t biYPelsPerMeter;
45 | uint32_t biClrUsed;
46 | uint32_t biClrImportant;
47 | uint32_t aColors[2]; // Palette data, first color is used if pixel bit is 0, second if pixel bit is 1
48 | } BMhdr_t;
49 |
50 | void charoutsmall(uint8_t theChar, uint8_t X, uint8_t Y) {
51 | // First of all, make lowercase into uppercase
52 | // (as there are no lowercase letters in the font)
53 | if ((theChar & 0x7f) >= 0x61 && (theChar & 0x7f) <= 0x7a) {
54 | theChar -= 0x20;
55 | }
56 | uint16_t fontoffset = ((theChar & 0x7f) - 0x20) * 6;
57 | uint8_t yoffset = Y & 0x7;
58 | Y >>= 3;
59 |
60 | #ifndef MINIMALISTIC
61 | uint8_t width = (theChar & 0x80) ? 7 : 6;
62 | #else
63 | uint8_t width=6;
64 | #endif
65 | for (uint8_t x = 0; x < width; x++) {
66 | uint16_t temp=smallfont[fontoffset++];
67 | #ifndef MINIMALISTIC
68 | if (theChar & 0x80) { temp ^= 0x7f; }
69 | #endif
70 | temp = temp << yoffset; // Shift pixel data to the correct lines
71 | uint16_t old = (FB[Y][X] | (FB[Y + 1][X] << 8));
72 | #ifndef MINIMALISTIC
73 | old &= ~(0x7f << yoffset); //Clean out old data
74 | #endif
75 | temp |= old; // Merge old data in FB with new char
76 | if (X >= (FB_WIDTH)) return; // make sure we don't overshoot
77 | if (Y < ((FB_HEIGHT / 8) - 0)) {
78 | FB[Y][X] = temp & 0xff;
79 | }
80 | if (Y < ((FB_HEIGHT / 8) - 1)) {
81 | FB[Y + 1][X] = temp >> 8;
82 | }
83 | X++;
84 | }
85 | }
86 |
87 | void LCD_disp_str(uint8_t* theStr, uint8_t theLen, uint8_t startx, uint8_t y, uint8_t theFormat) {
88 | #ifdef MINIMALISTIC
89 | for (uint8_t q = 0; q < theLen; q++) {
90 | charoutsmall(theStr[q], startx, y);
91 | startx += 6;
92 | }
93 | #else
94 | uint8_t invmask = theFormat & 0x80;
95 | for(uint8_t q = 0; q < theLen; q++) {
96 | charoutsmall(theStr[q] | invmask, startx, y);
97 | startx += 6;
98 | }
99 | #endif
100 | }
101 |
102 | void LCD_MultiLineH(uint8_t startx, uint8_t endx, uint64_t ymask) {
103 | for (uint8_t x = startx; x <= endx; x++) {
104 | FB[0][x] |= ymask & 0xff;
105 | FB[1][x] |= ymask >> 8;
106 | FB[2][x] |= ymask >> 16;
107 | FB[3][x] |= ymask >> 24;
108 | #if FB_HEIGHT == 64
109 | FB[4][x] |= ymask >> 32;
110 | FB[5][x] |= ymask >> 40;
111 | FB[6][x] |= ymask >> 48;
112 | FB[7][x] |= ymask >> 56;
113 | #endif
114 | }
115 | }
116 |
117 | /*
118 | * At the moment this is a very basic BMP file reader with the following limitations:
119 | * The bitmap must be 1-bit, uncompressed with a BITMAPINFOHEADER.
120 | */
121 | uint8_t LCD_BMPDisplay(uint8_t* thebmp, uint8_t xoffset, uint8_t yoffset) {
122 | BMhdr_t* bmhdr;
123 | uint8_t upsidedown = 1;
124 | uint8_t inverted = 0;
125 | uint16_t pixeloffset;
126 | uint8_t numpadbytes = 0;
127 |
128 | // The following code grabs the header portion of the bitmap and caches it locally on the stack
129 | BMhdr_t temp;
130 | uint8_t* xxx = (uint8_t*) &temp;
131 | for (uint16_t xx = 0; xx < sizeof(BMhdr_t); xx++) {
132 | xxx[xx] = *(thebmp + xx);
133 | }
134 | bmhdr = &temp;
135 |
136 | // printf("\n%s: bfSize=%x biSize=%x", __FUNCTION__, (uint16_t)bmhdr->bfSize, (uint16_t)bmhdr->biSize);
137 | // printf("\n%s: Image size is %d x %d", __FUNCTION__, (int16_t)bmhdr->biWidth, (int16_t)bmhdr->biHeight);
138 | if (bmhdr->biPlanes != 1 || bmhdr->biBitCount != 1 || bmhdr->biCompression != 0) {
139 | printf("\n%s: Incompatible bitmap format!", __FUNCTION__);
140 | return 1;
141 | }
142 | pixeloffset = bmhdr->bfOffBits;
143 | if (bmhdr->aColors[0] == 0) {
144 | inverted = 1;
145 | }
146 | if (bmhdr->biHeight<0) {
147 | bmhdr->biHeight = -bmhdr->biHeight;
148 | upsidedown = 0;
149 | }
150 | if ((bmhdr->biWidth+xoffset > FB_WIDTH) || (bmhdr->biHeight+yoffset > FB_HEIGHT)) {
151 | printf("\n%s: Image won't fit on display!", __FUNCTION__);
152 | return 1;
153 | }
154 |
155 | // Figure out how many dummy bytes that is present at the end of each line
156 | // If the image is 132 pixels wide then the pixel lines will be 20 bytes (160 pixels)
157 | // 132&31 is 4 which means that there are 3 bytes of padding
158 | numpadbytes = (4 - ((((bmhdr->biWidth) & 0x1f) + 7) >> 3)) & 0x03;
159 | // printf("\n%s: Skipping %d padding bytes after each line", __FUNCTION__, numpadbytes);
160 |
161 | for (int8_t y = bmhdr->biHeight - 1; y >= 0; y--) {
162 | uint8_t realY = upsidedown ? (uint8_t)y : (uint8_t)(bmhdr->biHeight) - y;
163 | realY += yoffset;
164 | uint8_t pagenum = realY >> 3;
165 | uint8_t pixelval = 1 << (realY & 0x07);
166 | for(uint8_t x = 0; x < bmhdr->biWidth; x += 8) {
167 | uint8_t pixel = *(thebmp + (pixeloffset++));
168 | if (inverted) { pixel^=0xff; }
169 | uint8_t max_b = bmhdr->biWidth - x;
170 | if (max_b>8) { max_b = 8; }
171 | for (uint8_t b = 0; b < max_b; b++) {
172 | if (pixel & 0x80) {
173 | FB[pagenum][x + b + xoffset] |= pixelval;
174 | }
175 | pixel = pixel << 1;
176 | }
177 | }
178 | pixeloffset += numpadbytes;
179 | }
180 | return 0;
181 | }
182 |
183 | void LCD_SetPixel(uint8_t x, uint8_t y) {
184 | if (x >= FB_WIDTH || y >= FB_HEIGHT) {
185 | // No random memory overwrites thank you
186 | return;
187 | }
188 | FB[y >> 3][x] |= 1 << (y & 0x07);
189 | }
190 |
191 | void LCD_SetBacklight(uint8_t backlight) {
192 | if (backlight) {
193 | FIO0SET = (1 << 11);
194 | } else {
195 | FIO0CLR = (1 << 11);
196 | }
197 | }
198 |
199 | #define UNTIL_BUSY_IS_CLEAR while (FIO1PIN & 0x800000); // Wait for busy to clear
200 |
201 | // No performance gain by inlining the command code
202 | static void LCD_WriteCmd(uint32_t cmdbyte) {
203 | // Start by making sure none of the display controllers are busy
204 | FIO1DIR = 0x000000; // Data pins are now inputs
205 | FIO0CLR = (1 << 22) | (1 << 13); // RS low, also make sure other CS is low
206 | FIO0SET = (1 << 12); // One CS at a time
207 | FIO0SET = (1 << 19); // RW must go high before E does
208 | FIO0SET = (1 << 18); // E high for read
209 | FIO1PIN; // Need 320ns of timing margin here
210 | FIO1PIN;
211 | FIO1PIN;
212 | FIO1PIN;
213 | FIO1PIN;
214 | FIO1PIN;
215 | UNTIL_BUSY_IS_CLEAR;
216 | FIO0CLR = (1 << 12); // Swap CS
217 | FIO0CLR = (1 << 18); // E low again
218 | FIO1PIN;
219 | FIO1PIN;
220 | FIO1PIN;
221 | FIO1PIN;
222 | FIO1PIN;
223 | FIO1PIN;
224 | FIO1PIN;
225 | FIO1PIN;
226 | FIO0SET = (1 << 13); // One CS at a time
227 | FIO0SET = (1 << 18); // E high for read
228 | FIO1PIN; // Need 320ns of timing margin here
229 | FIO1PIN;
230 | FIO1PIN;
231 | FIO1PIN;
232 | FIO1PIN;
233 | FIO1PIN;
234 | UNTIL_BUSY_IS_CLEAR;
235 | FIO0CLR = (1 << 19) | (1 << 18); // RW + E low again
236 | FIO1DIR = 0xff0000; // Data pins output again
237 |
238 | FIO0SET = (1 << 12) | (1 << 13); // Both CS active (one already activated above, doesn't matter)
239 | FIO1PIN = cmdbyte << 16; // Cmd on pins
240 | FIO1PIN; // Need ~200ns of timing margin here
241 | FIO1PIN;
242 | FIO1PIN;
243 | FIO1PIN;
244 | FIO1PIN;
245 | FIO1PIN;
246 | FIO0SET = (1 << 18); // E high
247 | FIO1PIN;
248 | FIO1PIN;
249 | FIO1PIN;
250 | FIO1PIN;
251 | FIO1PIN;
252 | FIO1PIN;
253 | FIO1PIN;
254 | FIO1PIN;
255 | FIO1PIN;
256 | FIO0CLR = (1 << 18); // E low
257 | FIO1PIN;
258 | FIO1PIN;
259 | FIO1PIN;
260 | FIO1PIN;
261 | FIO1PIN;
262 | FIO1PIN;
263 | FIO0SET = (1 << 22); // RS high
264 | }
265 |
266 | // Because of the cycle time requirements for E inlining actually does not boost performance
267 | //static inline void LCD_WriteData(uint32_t databyte, uint8_t chipnum) __attribute__((always_inline));
268 | static inline void LCD_WriteData(uint32_t databyte, uint8_t chipnum) {
269 | // Start by making sure that the correct controller is selected, then make sure it's not busy
270 | uint32_t csmask = chipnum ? (1 << 12) : (1 << 13);
271 | uint32_t csmask2 = chipnum ? (1 << 13) : (1 << 12);
272 | FIO0SET = csmask; // CS active
273 | FIO0CLR = csmask2; // CS inactive
274 | FIO1DIR = 0x000000; // Data pins are now inputs
275 | FIO0CLR = (1 << 22); // RS low
276 | FIO0SET = (1 << 19); // RW must go high before E does
277 | FIO0SET = (1 << 18); // E high for read
278 | FIO1PIN; // Need 320ns of timing margin here
279 | FIO1PIN;
280 | FIO1PIN;
281 | FIO1PIN;
282 | FIO1PIN;
283 | FIO1PIN;
284 | UNTIL_BUSY_IS_CLEAR;
285 | FIO0CLR = (1 << 18) | (1 << 19); // E and RW low
286 | FIO0SET = (1 << 22); // RS high
287 | FIO1DIR = 0xff0000; // Data pins output again
288 | FIO1PIN = databyte << 16; // Data on pins
289 | FIO1PIN; // Need ~200ns of timing margin here
290 | FIO1PIN;
291 | FIO1PIN;
292 | FIO1PIN;
293 | FIO1PIN;
294 | FIO1PIN;
295 | FIO0SET = (1 << 18); // E high
296 | FIO1PIN;
297 | FIO1PIN;
298 | FIO1PIN;
299 | FIO1PIN;
300 | FIO1PIN;
301 | FIO1PIN;
302 | FIO1PIN;
303 | FIO1PIN;
304 | FIO1PIN;
305 | FIO0CLR = (1 << 18); // E low
306 | /* When inlining additional padding needs to be done
307 | FIO1PIN;
308 | FIO1PIN;
309 | FIO1PIN;
310 | FIO1PIN;
311 | FIO1PIN;
312 | FIO1PIN;*/
313 | }
314 |
315 | #define LCD_ON (0x3f)
316 | #define LCD_RESET_X (0xb8)
317 | #define LCD_RESET_Y (0x40)
318 | #define LCD_RESET_STARTLINE (0xc0)
319 |
320 |
321 | void LCD_Init(void) {
322 | FIO1DIR = 0xff0000; // Data pins output
323 | LCD_WriteCmd(LCD_ON);
324 | LCD_WriteCmd(LCD_RESET_X);
325 | LCD_WriteCmd(LCD_RESET_Y);
326 | LCD_WriteCmd(LCD_RESET_STARTLINE);
327 | LCD_FB_Clear();
328 | LCD_FB_Update();
329 | LCD_SetBacklight(1);
330 | }
331 |
332 | void LCD_FB_Clear(void) {
333 | // Init FB storage
334 | for (uint8_t j = 0; j < (FB_HEIGHT / 8); j++) {
335 | memset(FB[j], 0, FB_WIDTH);
336 | }
337 | }
338 |
339 | void LCD_FB_Update() {
340 | for (uint32_t page = 0; page < (FB_HEIGHT >> 3); page++) {
341 | LCD_WriteCmd(LCD_RESET_X + page);
342 | LCD_WriteCmd(LCD_RESET_Y);
343 |
344 | for(uint32_t i = 0; i < 64; i++) {
345 | LCD_WriteData(FB[page][i], 0);
346 | LCD_WriteData(FB[page][i + 64], 1);
347 | }
348 | }
349 | }
350 |
--------------------------------------------------------------------------------
/src/lcd.h:
--------------------------------------------------------------------------------
1 | /*
2 | * lcd.h
3 | *
4 | * Created on: 14 nov 2014
5 | * Author: wjo
6 | */
7 |
8 | #ifndef LCD_H_
9 | #define LCD_H_
10 |
11 | #define FB_HEIGHT (64)
12 | #define FB_WIDTH (128)
13 |
14 | #define FONT6X6 (0)
15 | #define INVERT (0x80)
16 |
17 | #define LCD_CENTER (64)
18 | #define LCD_ALIGN_CENTER(x) (LCD_CENTER - (x * 3))
19 | #define LCD_ALIGN_RIGHT(x) (127 - (x * 6))
20 |
21 | void charoutsmall(uint8_t theChar, uint8_t X, uint8_t Y);
22 | void LCD_disp_str(uint8_t* theStr, uint8_t theLen, uint8_t startx, uint8_t y, uint8_t theFormat);
23 | void LCD_MultiLineH(uint8_t startx, uint8_t endx, uint64_t ymask);
24 | uint8_t LCD_BMPDisplay(uint8_t* thebmp, uint8_t xoffset, uint8_t yoffset);
25 | void LCD_SetPixel(uint8_t x, uint8_t y);
26 | void LCD_SetBacklight(uint8_t backlight);
27 | void LCD_Init(void);
28 | void LCD_FB_Clear(void);
29 | void LCD_FB_Update(void);
30 |
31 | #endif /* LCD_H_ */
32 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * main.c - T-962 reflow controller
3 | *
4 | * Copyright (C) 2014 Werner Johansson, wj@unifiedengineering.se
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 |
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 |
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include "LPC214x.h"
21 | #include
22 | #include
23 | #include
24 | #include "serial.h"
25 | #include "lcd.h"
26 | #include "io.h"
27 | #include "sched.h"
28 | #include "onewire.h"
29 | #include "adc.h"
30 | #include "i2c.h"
31 | #include "rtc.h"
32 | #include "eeprom.h"
33 | #include "keypad.h"
34 | #include "reflow.h"
35 | #include "reflow_profiles.h"
36 | #include "sensor.h"
37 | #include "buzzer.h"
38 | #include "nvstorage.h"
39 | #include "version.h"
40 | #include "vic.h"
41 | #include "max31855.h"
42 | #include "systemfan.h"
43 | #include "setup.h"
44 |
45 | extern uint8_t logobmp[];
46 | extern uint8_t stopbmp[];
47 | extern uint8_t selectbmp[];
48 | extern uint8_t editbmp[];
49 | extern uint8_t f3editbmp[];
50 |
51 | // No version.c file generated for LPCXpresso builds, fall back to this
52 | __attribute__((weak)) const char* Version_GetGitVersion(void) {
53 | return "no version info";
54 | }
55 |
56 | static char* format_about = \
57 | "\nT-962-controller open source firmware (%s)" \
58 | "\n" \
59 | "\nSee https://github.com/UnifiedEngineering/T-962-improvement for more details." \
60 | "\n" \
61 | "\nInitializing improved reflow oven...";
62 |
63 | static char* help_text = \
64 | "\nT-962-controller serial interface.\n\n" \
65 | " about Show about + debug information\n" \
66 | " bake Enter Bake mode with setpoint\n" \
67 | " bake