/******************************************************************************* * Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Thomas Holland - initial API and implementation *******************************************************************************/ package de.innot.avreclipse.ui.propertypages; import java.io.IOException; import java.text.MessageFormat; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.UIJob; import de.innot.avreclipse.AVRPlugin; import de.innot.avreclipse.core.avrdude.AVRDudeException; import de.innot.avreclipse.core.avrdude.AVRDudeSchedulingRule; import de.innot.avreclipse.core.properties.AVRDudeProperties; import de.innot.avreclipse.core.properties.AVRProjectProperties; import de.innot.avreclipse.core.toolinfo.AVRDude; import de.innot.avreclipse.core.toolinfo.GCC; import de.innot.avreclipse.core.util.AVRMCUidConverter; import de.innot.avreclipse.ui.dialogs.AVRDudeErrorDialogJob; /** * This tab handles setting of all target hardware related properties. * * @author Thomas Holland * @since 2.2 * */ public class TabTargetHardware extends AbstractAVRPropertyTab { private static final String LABEL_MCUTYPE = "MCU Type"; private static final String LABEL_FCPU = "MCU Clock Frequency"; private static final String TEXT_LOADBUTTON = "Load from MCU"; private static final String TEXT_LOADBUTTON_BUSY = "Loading..."; private final static String TITLE_FUSEBYTEWARNING = "{0} Conflict"; private final static String TEXT_FUSEBYTEWARNING = "Selected MCU is not compatible with the currently set {0}.\n" + "Please check the {0} settings on the AVRDude {1}."; private final static String[] TITLEINSERT = new String[] { "", "Fuse Byte", "Lockbits", "Fuse Byte and Lockbits" }; private final static String[] TEXTINSERT = new String[] { "", "fuse byte", "lockbits", "fuse byte and lockbits" }; private final static String[] TABNAMEINSERT = new String[] { "", "Fuse tab", "Lockbits tab", "Fuse and Lockbit tabs" }; /** List of common MCU frequencies (taken from mfile) */ private static final String[] FCPU_VALUES = { "1000000", "1843200", "2000000", "3686400", "4000000", "7372800", "8000000", "11059200", "14745600", "16000000", "18432000", "20000000" }; /** The Properties that this page works with */ private AVRProjectProperties fTargetProps; private Combo fMCUcombo; private Button fLoadButton; private Composite fMCUWarningComposite; private Combo fFCPUcombo; private Set<String> fMCUids; private String[] fMCUNames; private String fOldMCUid; private String fOldFCPU; private static final Image IMG_WARN = PlatformUI .getWorkbench() .getSharedImages() .getImage( ISharedImages.IMG_OBJS_WARN_TSK); /* * (non-Javadoc) * @see * org.eclipse.cdt.ui.newui.AbstractCPropertyTab#createControls(org.eclipse.swt.widgets.Composite * ) */ @Override public void createControls(Composite parent) { super.createControls(parent); usercomp.setLayout(new GridLayout(4, false)); // Get the list of supported MCU id's from the compiler // The list is then converted into an array of MCU names // // If we ever implement per project paths this needs to be moved to the // updataData() method to reload the list of supported mcus every time // the paths change. The list is added to the combo in addMCUSection(). if (fMCUids == null) { try { fMCUids = GCC.getDefault().getMCUList(); } catch (IOException e) { // Could not start avr-gcc. Pop an Error Dialog and continue with an empty list IStatus status = new Status( IStatus.ERROR, AVRPlugin.PLUGIN_ID, "Could not execute avr-gcc. Please check the AVR paths in the preferences.", e); ErrorDialog.openError(usercomp.getShell(), "AVR-GCC Execution fault", null, status); fMCUids = new HashSet<String>(); } String[] allmcuids = fMCUids.toArray(new String[fMCUids.size()]); fMCUNames = new String[fMCUids.size()]; for (int i = 0; i < allmcuids.length; i++) { fMCUNames[i] = AVRMCUidConverter.id2name(allmcuids[i]); } Arrays.sort(fMCUNames); } addMCUSection(usercomp); addFCPUSection(usercomp); addSeparator(usercomp); } private void addMCUSection(Composite parent) { GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1); FontMetrics fm = getFontMetrics(parent); gd.widthHint = Dialog.convertWidthInCharsToPixels(fm, 20); // MCU Selection Combo setupLabel(parent, LABEL_MCUTYPE, 1, SWT.NONE); // Label label = new Label(parent, SWT.NONE); // label.setText(LABEL_MCUTYPE); // label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); fMCUcombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); fMCUcombo.setLayoutData(gd); fMCUcombo.setItems(fMCUNames); fMCUcombo.setVisibleItemCount(Math.min(fMCUNames.length, 20)); fMCUcombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String mcuname = fMCUcombo.getItem(fMCUcombo.getSelectionIndex()); String mcuid = AVRMCUidConverter.name2id(mcuname); fTargetProps.setMCUId(mcuid); // Check if supported by avrdude and set the errorpane as // required checkAVRDude(mcuid); // Check fuse byte settings and pop a message if the settings // are not compatible checkFuseBytes(mcuid); } }); // Load from Device Button fLoadButton = setupButton(parent, TEXT_LOADBUTTON, 1, SWT.NONE); fLoadButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); fLoadButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { loadComboFromDevice(); } }); // Dummy Label for Padding Label label = new Label(parent, SWT.NONE); label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); // The Warning Composite fMCUWarningComposite = new Composite(parent, SWT.NONE); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 4; fMCUWarningComposite.setLayoutData(gd); GridLayout gl = new GridLayout(2, false); gl.marginHeight = 0; gl.marginWidth = 0; gl.verticalSpacing = 0; gl.horizontalSpacing = 0; fMCUWarningComposite.setLayout(gl); Label warnicon = new Label(fMCUWarningComposite, SWT.LEFT); warnicon.setLayoutData(new GridData(GridData.BEGINNING)); warnicon.setImage(IMG_WARN); Label warnmessage = new Label(fMCUWarningComposite, SWT.LEFT); warnmessage.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); warnmessage.setText("This MCU is not supported by AVRDude"); fMCUWarningComposite.setVisible(false); } private void addFCPUSection(Composite parent) { GridData gd = new GridData(); FontMetrics fm = getFontMetrics(parent); gd.widthHint = Dialog.convertWidthInCharsToPixels(fm, 14); setupLabel(parent, LABEL_FCPU, 1, SWT.NONE); fFCPUcombo = new Combo(parent, SWT.DROP_DOWN); fFCPUcombo.setLayoutData(gd); fFCPUcombo.setTextLimit(8); // max. 99 MHz fFCPUcombo.setToolTipText("Target Hardware Clock Frequency in Hz"); fFCPUcombo.setVisibleItemCount(FCPU_VALUES.length); fFCPUcombo.setItems(FCPU_VALUES); fFCPUcombo.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { if (fTargetProps != null) { fTargetProps.setFCPU(fFCPUcombo.getText()); } } }); // Ensure that only integer values are entered fFCPUcombo.addVerifyListener(new VerifyListener() { public void verifyText(VerifyEvent event) { String text = event.text; if (!text.matches("[0-9]*")) { event.doit = false; } } }); } /* * (non-Javadoc) * @see * de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#performApply(de.innot.avreclipse * .core.preferences.AVRConfigurationProperties, * de.innot.avreclipse.core.preferences.AVRConfigurationProperties) */ @Override protected void performApply(AVRProjectProperties dst) { if (fTargetProps == null) { // Do nothing if the Target properties do not exist. return; } String newMCUid = fTargetProps.getMCUId(); String newFCPU = fTargetProps.getFCPU(); dst.setMCUId(newMCUid); dst.setFCPU(newFCPU); // Check if a rebuild is required boolean rebuild = setRebuildRequired(); if (rebuild) { setDiscoveryRequired(); } fOldMCUid = newMCUid; fOldFCPU = newFCPU; } /* * (non-Javadoc) * @see * de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#performDefaults(de.innot.avreclipse * .core.preferences.AVRProjectProperties) */ @Override protected void performCopy(AVRProjectProperties defaults) { fTargetProps.setMCUId(defaults.getMCUId()); fTargetProps.setFCPU(defaults.getFCPU()); updateData(fTargetProps); } /* * (non-Javadoc) * @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#performOK() */ @Override protected void performOK() { // We override this to set the rebuild state as required boolean rebuild = setRebuildRequired(); if (rebuild) { // Now we need to invalidate all discovered Symbols, because they still contain infos // about the previous MCU. setDiscoveryRequired(); } super.performOK(); } /* * (non-Javadoc) * @see * de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#updateData(de.innot.avreclipse * .core.preferences.AVRConfigurationProperties) */ @Override protected void updateData(AVRProjectProperties cfg) { fTargetProps = cfg; String mcuid = cfg.getMCUId(); fMCUcombo.select(fMCUcombo.indexOf(AVRMCUidConverter.id2name(mcuid))); checkAVRDude(mcuid); String fcpu = cfg.getFCPU(); fFCPUcombo.setText(fcpu); // Save the original values, so we can set the rebuild flag when any // changes are applied. if (fOldMCUid == null) { fOldMCUid = mcuid; fOldFCPU = fcpu; } } /** * Check if the given MCU is supported by avrdude and set visibility of the MCU Warning Message * accordingly. * * @param mcuid * The MCU id value to test */ private void checkAVRDude(String mcuid) { if (AVRDude.getDefault().hasMCU(mcuid)) { fMCUWarningComposite.setVisible(false); } else { fMCUWarningComposite.setVisible(true); } } /** * Check if the FuseBytesProperties and Lockbits in the current properties are compatible with * the selected mcu. If not, a warning dialog is shown. */ private void checkFuseBytes(String mcuid) { AVRDudeProperties avrdudeprops = fTargetProps.getAVRDudeProperties(); // State: // 0x00 = neither fuses nor lockbits are written // 0x01 = fuses not compatible // 0x02 = lockbits not compatible // 0x03 = both not compatible // The state is used as an index to the String arrays with the texts. int state = 0x00; // Check fuse bytes boolean fusewrite = avrdudeprops.getFuseBytes(getCfg()).getWrite(); if (fusewrite) { boolean fusecompatible = avrdudeprops.getFuseBytes(getCfg()).isCompatibleWith(mcuid); if (!fusecompatible) { state |= 0x01; } } // check lockbits boolean lockwrite = avrdudeprops.getLockbitBytes(getCfg()).getWrite(); if (lockwrite) { boolean lockcompatible = avrdudeprops.getLockbitBytes(getCfg()).isCompatibleWith(mcuid); if (!lockcompatible) { state |= 0x02; } } if (!fusewrite && !lockwrite) { // Neither Fuses nor Lockbits are written, so no need for a warning. // The fuses tab respective lockbits tab will show a warning once the write flag is // changed. return; } if (state == 0) { // both fuses and lockbits are compatible, so no need for a warning. return; } // Now show the warning. String title = MessageFormat.format(TITLE_FUSEBYTEWARNING, TITLEINSERT[state]); String text = MessageFormat.format(TEXT_FUSEBYTEWARNING, TEXTINSERT[state], TABNAMEINSERT[state]); MessageDialog.openWarning(fMCUcombo.getShell(), title, text); } /** * Checks if the current target values are different from the original ones and set the rebuild * flag for the configuration / project if yes. */ private boolean setRebuildRequired() { if (fOldMCUid == null || fOldFCPU == null || !(fTargetProps.getMCUId().equals(fOldMCUid)) || !(fTargetProps.getFCPU().equals(fOldFCPU))) { setRebuildState(true); return true; } return false; } /** * Load the actual MCU from the currently selected Programmer and set the MCU combo accordingly. * <p> * This method will start a new Job to load the values and return immediately. * </p> */ private void loadComboFromDevice() { // Disable the Load Button. It is re-enabled by the load job when it finishes. fLoadButton.setEnabled(false); fLoadButton.setText(TEXT_LOADBUTTON_BUSY); // The Job that does the actual loading. Job readJob = new Job("Reading MCU Signature") { @Override protected IStatus run(IProgressMonitor monitor) { try { monitor.beginTask("Starting AVRDude", 100); final String mcuid = AVRDude.getDefault().getAttachedMCU( fTargetProps.getAVRDudeProperties().getProgrammer(), new SubProgressMonitor(monitor, 95)); fTargetProps.setMCUId(mcuid); // and update the user interface if (!fLoadButton.isDisposed()) { fLoadButton.getDisplay().syncExec(new Runnable() { public void run() { updateData(fTargetProps); // Check if supported by avrdude and set the errorpane as // required checkAVRDude(mcuid); // Check fuse byte settings and pop a message if the settings // are not compatible checkFuseBytes(mcuid); } }); } monitor.worked(5); } catch (AVRDudeException ade) { // Show an Error message and exit if (!fLoadButton.isDisposed()) { UIJob messagejob = new AVRDudeErrorDialogJob(fLoadButton.getDisplay(), ade, fTargetProps.getAVRDudeProperties().getProgrammerId()); messagejob.setPriority(Job.INTERACTIVE); messagejob.schedule(); try { messagejob.join(); // block until the dialog is closed. } catch (InterruptedException e) { // Don't care if the dialog is interrupted from outside. } } } catch (SWTException swte) { // The display has been disposed, so the user is not // interested in the results from this job return Status.CANCEL_STATUS; } finally { monitor.done(); // Enable the Load from MCU Button if (!fLoadButton.isDisposed()) { fLoadButton.getDisplay().syncExec(new Runnable() { public void run() { // Re-Enable the Button fLoadButton.setEnabled(true); fLoadButton.setText(TEXT_LOADBUTTON); } }); } } return Status.OK_STATUS; } }; // now set the Job properties and start it readJob.setRule(new AVRDudeSchedulingRule(fTargetProps.getAVRDudeProperties() .getProgrammer())); readJob.setPriority(Job.SHORT); readJob.setUser(true); readJob.schedule(); } }