Ejemplo n.º 1
0
/************************************************************************
Configuration of the Pulse Width Modulation (PWM).
************************************************************************/
void PWMInit(void){
	pmc_enable_periph_clk(ID_PWM);
	pwm_channel_disable(PWM, PWM_CHANNEL_3);
	pwm_channel_disable(PWM, PWM_CHANNEL_2);
	
	pwm_clock_t clock_setting = {
		.ul_clka = 50 * 19999,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM, &clock_setting);
	
	InitPIN40();
	InitPIN38();
}

/************************************************************************
Initiation of digital pin 40 on the Arduino Due board for PWM.
************************************************************************/
void InitPIN40(void)
{
	PWMPin40.channel = PWM_CHANNEL_3;
	PWMPin40.ul_prescaler = PWM_CMR_CPRE_CLKA;
	PWMPin40.ul_duty = 0;
	PWMPin40.ul_period = 19999;
	pwm_channel_init(PWM, &PWMPin40);
	pio_set_peripheral(PIOC, PIO_PERIPH_B, PIO_PC8B_PWML3);
	pwm_channel_enable(PWM, PWM_CHANNEL_3);
}
Ejemplo n.º 2
0
/* Funktionen initierar PWM-signalen */
void pwm_setup(void)
{
	pmc_enable_periph_clk(ID_PWM);
	pwm_channel_disable(PWM, PWM_CHANNEL);
	
	pwm_clock_t clock_setting = {
		.ul_clka = PWM_FREQUENCY * PWM_PERIOD,
		.ul_clkb = 0,
		.ul_mck = SYS_CLOCK
	};
	
	pwm_init(PWM, &clock_setting);
	
	pwm_channel_instance.alignment = PWM_ALIGN_LEFT;
	pwm_channel_instance.polarity = PWM_LOW;
	pwm_channel_instance.ul_prescaler = PWM_CMR_CPRE_CLKA;
	pwm_channel_instance.ul_period = PWM_PERIOD;
	pwm_channel_instance.ul_duty = PWM_INIT_DUTY_CYCLE;
	pwm_channel_instance.channel = PWM_CHANNEL;
	
	pwm_channel_init(PWM, &pwm_channel_instance);
	pio_set_peripheral(PWM_PIO, PWM_PERIPHERAL, PWM_PIN);
	pwm_channel_enable(PWM, PWM_CHANNEL);
}


/* Funktionen uppdaterar PWM-signalens duty-cycle */
void update_pwm(int ul_duty)
{
	// Kontrollerar så att angiven duty-cycle befinner sig inom rätt område (0-1000).
	if(ul_duty < PWM_MIN_DUTY_CYCLE)
	{
		pwm_channel_update_duty(PWM, &pwm_channel_instance, PWM_MIN_DUTY_CYCLE);
	}
	else if(ul_duty > PWM_MAX_DUTY_CYCLE)
	{
		pwm_channel_update_duty(PWM, &pwm_channel_instance, PWM_MAX_DUTY_CYCLE);
	}
	else
	{
		pwm_channel_update_duty(PWM, &pwm_channel_instance, ul_duty);
	}
}


/* Funktionen initierar motor-shielden */
void motor_shield_setup(void)
{
	ioport_set_pin_dir(PIO_PD8_IDX, IOPORT_DIR_OUTPUT);
	ioport_set_pin_level(PIO_PD8_IDX, IOPORT_PIN_LEVEL_HIGH);
	
	ioport_set_pin_dir(PIO_PC21_IDX, IOPORT_DIR_OUTPUT);
	ioport_set_pin_level(PIO_PC21_IDX, IOPORT_PIN_LEVEL_LOW);
}
static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
{
	struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);

	if (pwmbl->gpio_on != -1)
		gpio_set_value(pwmbl->gpio_on, 0);
	pwm_channel_disable(&pwmbl->pwmc);
	pwm_channel_free(&pwmbl->pwmc);
	backlight_device_unregister(pwmbl->bldev);
	platform_set_drvdata(pdev, NULL);

	return 0;
}
Ejemplo n.º 4
0
int mon_calibrate(int argc, char **argv){
  int on_time, off_time;

  //check if we should stop calibration mode
  if(argc==2){
    if(strcmp(argv[1],"stop")==0){
      printf("stopping calibration mode\n");
      wemo_config.calibrate = false;
      //disable the calibration PWM
      pwm_channel_disable_interrupt(PWM,CAL_PWM_CHANNEL,CAL_PWM_CHANNEL);
      pwm_channel_disable(PWM,CAL_PWM_CHANNEL);
      //save the new config
      fs_write_config();
      return 0;
    } else {
      printf("usage: specify [stop] or [start # #]\n");
    }
    return -1;
  }
  //make sure there are enough params to start 
  if(argc!=4 || (strcmp(argv[1],"start")!=0)){
    printf("usage: specify [stop] or \n");
    printf("\t [start on_time off_time] in ms\n");
    return -1;
  }
  //parse the on_time and off_time params
  on_time = atoi(argv[2]);
  off_time = atoi(argv[3]);
  //make sure these times are valid
  if(on_time<MIN_CAL_TIME || off_time<MIN_CAL_TIME){
    printf("times must be >= %d\n",MIN_CAL_TIME);
    return -1;
  }
  //stop data collection
  wemo_config.collect_data = false;
  //setup calibration parameters
  wemo_config.calibrate = true;
  wemo_config.cal_on_time = on_time;
  wemo_config.cal_off_time = off_time;
  //start the calibration PWM
  pwm_channel_enable_interrupt(PWM,CAL_PWM_CHANNEL,CAL_PWM_CHANNEL);
  pwm_channel_enable(PWM,CAL_PWM_CHANNEL);
  //save the new config
  fs_write_config();

  return 0;
}
Ejemplo n.º 5
0
static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
{
	struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
	int intensity = bd->props.brightness;
	int pwm_duty;

	if (bd->props.power != FB_BLANK_UNBLANK)
		intensity = 0;
	if (bd->props.fb_blank != FB_BLANK_UNBLANK)
		intensity = 0;

	if (pwmbl->pdata->pwm_active_low)
		pwm_duty = pwmbl->pdata->pwm_duty_min + intensity;
	else
		pwm_duty = pwmbl->pdata->pwm_duty_max - intensity;

	if (pwm_duty > pwmbl->pdata->pwm_duty_max)
		pwm_duty = pwmbl->pdata->pwm_duty_max;
	if (pwm_duty < pwmbl->pdata->pwm_duty_min)
		pwm_duty = pwmbl->pdata->pwm_duty_min;

	if (!intensity) {
		if (pwmbl->gpio_on != -1) {
			gpio_set_value(pwmbl->gpio_on,
					0 ^ pwmbl->pdata->on_active_low);
		}
		pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
		pwm_channel_disable(&pwmbl->pwmc);
	} else {
		pwm_channel_enable(&pwmbl->pwmc);
		pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
		if (pwmbl->gpio_on != -1) {
			gpio_set_value(pwmbl->gpio_on,
					1 ^ pwmbl->pdata->on_active_low);
		}
	}

	return 0;
}
Ejemplo n.º 6
0
// Set the audio mode
void audioModeSet(AudioMode m) {
	// Don't do anything if mode is not changed
	if (mode == m) {
		return;
	}
	
	// Set new mode
	mode = m;
	audioFrequencySet(0); // Always reset currFreq on mode change
	switch (m) {
		case AM_OFF:
			pwm_channel_disable(PWM, PWM_CHANNEL_2);
			dacc_disable_channel(DACC, 0);
			break;
		case AM_ADC:
			pwm_channel_enable(PWM, PWM_CHANNEL_2);
			dacc_enable_channel(DACC, 0);
			break;
		case AM_HZ:
			pwm_channel_enable(PWM, PWM_CHANNEL_2);
			dacc_enable_channel(DACC, 0);
			break;
	}
}
Ejemplo n.º 7
0
/**
 * \brief Application entry point for PWM PDC example.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{
	uint32_t i;
	uint8_t uc_key;
	uint8_t c_numkey;

	/* Initialize the SAM system */
	sysclk_init();
	board_init();

	/* Configure the console uart for debug infomation */
	configure_console();

	/* Output example information */
	puts(STRING_HEADER);

	/* Enable PWM peripheral clock */
	pmc_enable_periph_clk(ID_PWM);

	/* Disable PWM channel of LED1 and LED0 */
	pwm_channel_disable(PWM, PIN_PWM_LED0_CHANNEL);
	pwm_channel_disable(PWM, PIN_PWM_LED1_CHANNEL);

	/*
	 * In PWM synchronisation mode the channel0 is used as reference channel,
	 * so it is necessary to disable, configure and enable it.
	 */
	if (PIN_PWM_LED0_CHANNEL && PIN_PWM_LED1_CHANNEL) {
		pwm_channel_disable(PWM, 0);
	}

	/* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */
	pwm_clock_t clock_setting = {
		.ul_clka = PWM_FREQUENCY * PERIOD_VALUE,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM, &clock_setting);

	/* Initialize PWM channels outputs */
	pwm_output_t channel_output = {
		/* Disable override PWMH outputs */
		.b_override_pwmh = false,
		/* Disable override PWML outputs */
		.b_override_pwml = false,
		/* Set override PWMH output level as HIGH */
		.override_level_pwmh = PWM_HIGH,
		/* Set override PWML output level as LOW */
		.override_level_pwml = PWM_LOW
	};

	/* Initialize PWM synchronous channels */
	pwm_channel_t sync_channel = {
		/* Use PWM clock A as source clock */
		.ul_prescaler = PWM_CMR_CPRE_CLKA,
		/* Period value of output waveform */
		.ul_period = PERIOD_VALUE,
		/* Duty cycle value of output waveform */
		.ul_duty = INIT_DUTY_VALUE,
		/* Set it as a synchronous channel */
		.b_sync_ch = true,
		/* Enable dead-time generator */
		.b_deadtime_generator = true,
		/* Dead-time value for PWMH output */
		.us_deadtime_pwmh = INIT_DEAD_TIME,
		/* Dead-time value for PWML output */
		.us_deadtime_pwml = INIT_DEAD_TIME,
		/* Disable override PWMH outputs */
		.output_selection.b_override_pwmh = false,
		/* Disable override PWML outputs */
		.output_selection.b_override_pwml = false
	};

	/*
	 * In PWM synchronisation mode the channel0 is used as reference channel,
	 * so it is necessary to disable, configure and enable it.
	 */
	if (PIN_PWM_LED0_CHANNEL && PIN_PWM_LED1_CHANNEL) {
		sync_channel.channel = 0;
		pwm_channel_init(PWM, &sync_channel);
	}

	/* Initialize PWM channel of LED1 */
	sync_channel.channel = PIN_PWM_LED1_CHANNEL;
	pwm_channel_init(PWM, &sync_channel);

	/* Initialize PWM channel of LED2 */
	sync_channel.channel = PIN_PWM_LED0_CHANNEL;
	pwm_channel_init(PWM, &sync_channel);

	/*
	 * Initialize PWM synchronous channels
	 * Synchronous Update Mode: Automatic update duty cycle value by the PDC
	 * and automatic update of synchronous channels. The update occurs when
	 * the Update Period elapses (MODE 2).
	 * Synchronous Update Period = MAX_SYNC_UPDATE_PERIOD.
	 */
	pwm_sync_init(PWM, PWM_SYNC_UPDATE_MODE_2, MAX_SYNC_UPDATE_PERIOD);

	/*
	 * Request PDC transfer as soon as the synchronous update period elapses
	 * (comparison unit is ignored).
	 */
	pwm_pdc_set_request_mode(PWM, PWM_PDC_UPDATE_PERIOD_ELAPSED, (1 << 0));

	/* Configure interrupt for PDC transfer */
	NVIC_DisableIRQ(PWM_IRQn);
	NVIC_ClearPendingIRQ(PWM_IRQn);
	NVIC_SetPriority(PWM_IRQn, 0);
	NVIC_EnableIRQ(PWM_IRQn);
	pwm_pdc_enable_interrupt(PWM, PWM_PDC_TX_END);

	/* Fill duty cycle buffer for channel #0 and #1 */
	/*
	 * For PWM channel 0 and 1, duty cycle ranges from
	 * MIN_DUTY_CYCLE to MAX_DUTY_CYCLE
	 */
	for (i = 0; i < (DUTY_BUFFER_LENGTH / 3); i++) {
		g_us_duty_buffer[i * 3] = (i + INIT_DUTY_VALUE);
		g_us_duty_buffer[i * 3 + 1] = (i + INIT_DUTY_VALUE);
		g_us_duty_buffer[i * 3 + 2] = (i + INIT_DUTY_VALUE);
	}

	/* Configure the PDC transfer packet and enable PDC transfer */
	g_pdc_tx_packet.ul_addr = (uint32_t) (&(g_us_duty_buffer[0]));
	g_pdc_tx_packet.ul_size = DUTY_BUFFER_LENGTH;
	pdc_tx_init(PDC_PWM, &g_pdc_tx_packet, 0);
	pdc_enable_transfer(PDC_PWM, PERIPH_PTCR_TXTEN);

	/* Enable all synchronous channels by enabling channel 0 */
	/*
	 * In PWM synchronisation mode the channel0 is used as reference channel,
	 * so it is necessary to disable, configure and enable it.
	 */
	if (PIN_PWM_LED0_CHANNEL && PIN_PWM_LED1_CHANNEL) {
		pwm_channel_enable(PWM, 0);
	}
	pwm_channel_enable(PWM, PIN_PWM_LED0_CHANNEL);
	pwm_channel_enable(PWM, PIN_PWM_LED1_CHANNEL);

	while (1) {
		display_menu();
		while (uart_read(CONSOLE_UART, &uc_key));

		switch (uc_key) {
		case 'u':
		case 'U':
			printf("Input update period must be between 0 and %d.\r\n",
					(int)MAX_SYNC_UPDATE_PERIOD);
			c_numkey = get_num_value();
			if (c_numkey <= MAX_SYNC_UPDATE_PERIOD) {
				/* Set new synchronous update period value */
				pwm_sync_change_period(PWM, c_numkey);
				printf("Synchronous update period has been changed to %d.\r\n", (int)c_numkey);

			} else {
				printf("Wrong value, expected value must be one of 0 to %d!\r\n", (int)MAX_SYNC_UPDATE_PERIOD);
			}
			break;
		case 'd':
		case 'D':
			printf("Input dead time for channel #0 must be between %d and %d.\r\n", INIT_DUTY_VALUE, PERIOD_VALUE);
			c_numkey = get_num_value();

			if (c_numkey <= PERIOD_VALUE) {
				/* Set new dead time value for channel 0 */
				pwm_channel_update_dead_time(PWM, &sync_channel,
						c_numkey, c_numkey);
				/* Update all synchronous channels */
				pwm_sync_unlock_update(PWM);
				printf("Dead time has been changed to %d.\r\n", (int)c_numkey);

			} else {
				printf("Wrong value, expected value must be one of %d to %d!\r\n", INIT_DUTY_VALUE, PERIOD_VALUE);
			}
			break;
		case 'o':
		case 'O':
			if (channel_output.b_override_pwmh
					&& channel_output.b_override_pwml) {
				/* Disable override outputs of channel 0 synchronously */
				channel_output.b_override_pwmh = false;
				channel_output.b_override_pwml = false;
				pwm_channel_update_output(PWM, &sync_channel,
						&channel_output, true);
				puts("PWM Channel #0 output override has been disabled.\r\n");
			}
			else {
				/* Enable override outputs of channel 0 synchronously */
				channel_output.b_override_pwmh = true;
				channel_output.b_override_pwml = true;
				pwm_channel_update_output(PWM, &sync_channel,
						&channel_output, true);
				puts("PWM Channel #0 output has been overridden.\r\n");

			}
			break;
		default:
			puts("Unknown input!\r\n");
			break;
		}
	}
}
Ejemplo n.º 8
0
/**
 * \brief Configure to trigger ADC by PWM Event Line.
 */
static void configure_pwm_trigger(void)
{
	/* PWM frequency in Hz. */
#define PWM_FREQUENCY               2
	/* Maximum duty cycle value. */
#define MAX_DUTY_CYCLE              1000

	/* Enable PWMC peripheral clock. */
	pmc_enable_periph_clk(ID_PWM);

	/* Disable PWM channel 0. */
	pwm_channel_disable(PWM, PWM_CHANNEL_0);

	gpio_configure_pin(PIN_PWMC_PWMH0_TRIG, PIN_PWMC_PWMH0_TRIG_FLAG);

	/* Set clock A to run at PWM_FREQUENCY * MAX_DUTY_CYCLE (clock B is not used). */
	pwm_clock_t pwm_clock_setting = {
		.ul_clka = PWM_FREQUENCY * MAX_DUTY_CYCLE,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM, &pwm_clock_setting);

	/* Configure PWMC for channel 0 (left-aligned). */
		pwm_channel_t pwm_trigger_channel = {
			.channel = PWM_CHANNEL_0,
			.alignment = PWM_ALIGN_LEFT,
			.polarity = PWM_LOW,
		.ul_prescaler = PWM_CMR_CPRE_CLKA,
		.ul_period = MAX_DUTY_CYCLE,
		.ul_duty = MAX_DUTY_CYCLE / 2
	};
	pwm_channel_init(PWM, &pwm_trigger_channel);

	pwm_cmp_t pwm_comparison_setting = {
		.unit = PWM_CMP_UNIT_0,
		.b_enable = true,
		.ul_value = MAX_DUTY_CYCLE / 2,
		.b_pulse_on_line_0 = true
	};
	pwm_cmp_init(PWM, &pwm_comparison_setting);


	/* Enable PWM channel 0. */
	pwm_channel_enable(PWM, PWM_CHANNEL_0);
	/* Set PWM Event Line 0 trigger. */
#if SAM3S || SAM3XA || SAM4S
	adc_configure_trigger(ADC, ADC_TRIG_PWM_EVENT_LINE_0, 0);
#elif SAM3U
#ifdef ADC_12B
	adc12b_configure_trigger(ADC12B, ADC12B_TRIG_PWM_EVENT_LINE_0);
#else
	adc_configure_trigger(ADC, ADC_TRIG_PWM_EVENT_LINE_0);
#endif
#endif
}
#endif

/**
 * \brief Read converted data through PDC channel.
 *
 * \param p_adc The pointer of adc peripheral.
 * \param p_s_buffer The destination buffer.
 * \param ul_size The size of the buffer.
 */
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
static uint32_t adc_read_buffer(Adc * p_adc, uint16_t * p_s_buffer, uint32_t ul_size)
{
	/* Check if the first PDC bank is free. */
	if ((p_adc->ADC_RCR == 0) && (p_adc->ADC_RNCR == 0)) {
		p_adc->ADC_RPR = (uint32_t) p_s_buffer;
		p_adc->ADC_RCR = ul_size;
		p_adc->ADC_PTCR = ADC_PTCR_RXTEN;

		return 1;
	} else { /* Check if the second PDC bank is free. */
		if (p_adc->ADC_RNCR == 0) {
			p_adc->ADC_RNPR = (uint32_t) p_s_buffer;
			p_adc->ADC_RNCR = ul_size;

			return 1;
		} else {
			return 0;
		}
	}
}
#elif SAM3U
#ifdef ADC_12B
static uint32_t adc12_read_buffer(Adc12b * p_adc, uint16_t * p_s_buffer,
		uint32_t ul_size)
{
	/* Check if the first PDC bank is free. */
	if ((p_adc->ADC12B_RCR == 0) && (p_adc->ADC12B_RNCR == 0)) {
		p_adc->ADC12B_RPR = (uint32_t) p_s_buffer;
		p_adc->ADC12B_RCR = ul_size;
		p_adc->ADC12B_PTCR = ADC12B_PTCR_RXTEN;

		return 1;
	} else {	/* Check if the second PDC bank is free. */
		if (p_adc->ADC12B_RNCR == 0) {
			p_adc->ADC12B_RNPR = (uint32_t) p_s_buffer;
			p_adc->ADC12B_RNCR = ul_size;

			return 1;
		} else {
			return 0;
		}
	}
}
#else
static uint32_t adc_read_buffer(Adc * p_adc, uint16_t * p_s_buffer, uint32_t ul_size)
{
	/* Check if the first PDC bank is free. */
	if ((p_adc->ADC_RCR == 0) && (p_adc->ADC_RNCR == 0)) {
		p_adc->ADC_RPR = (uint32_t) p_s_buffer;
		p_adc->ADC_RCR = ul_size;
		p_adc->ADC_PTCR = ADC_PTCR_RXTEN;

		return 1;
	} else {	/* Check if the second PDC bank is free. */
		if (p_adc->ADC_RNCR == 0) {
			p_adc->ADC_RNPR = (uint32_t) p_s_buffer;
			p_adc->ADC_RNCR = ul_size;

			return 1;
		} else {
			return 0;
		}
	}
}
#endif
#endif

/**
 * \brief Start ADC sample.
 * Initialize ADC, set clock and timing, and set ADC to given mode.
 */
static void start_adc(void)
{
	/* Enable peripheral clock. */
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
	uint32_t i;
	pmc_enable_periph_clk(ID_ADC);
#elif SAM3U
#ifdef ADC_12B
	pmc_enable_periph_clk(ID_ADC12B);
#else
	pmc_enable_periph_clk(ID_ADC);
#endif
#endif

	/* Initialize ADC. */
	/*
	 * Formula: ADCClock = MCK / ( (PRESCAL+1) * 2 )
	 * For example, MCK = 64MHZ, PRESCAL = 4, then:
	 * ADCClock = 64 / ((4+1) * 2) = 6.4MHz;
	 */
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
	/* Formula:
	 *     Startup  Time = startup value / ADCClock
	 *     Startup time = 64 / 6.4MHz = 10 us
	 */
	adc_init(ADC, sysclk_get_cpu_hz(), 6400000, ADC_STARTUP_TIME_4);
#elif SAM3U
#ifdef ADC_12B
	/* Formula:
	 *     Startup  Time = (startup value + 1) * 8 / ADCClock
	 *     Startup time = (7 + 1) * 8 / 6.4MHz = 10 us
	 */
	adc12b_init(ADC12B, sysclk_get_cpu_hz(), 6400000, STARTUP_TIME, OFF_MODE_STARTUP_TIME);
#else
	/* Formula:
	 *     Startup  Time = (startup value + 1) * 8 / ADCClock
	 *     Startup time = (3 + 1) * 8 / 3.2MHz = 10 us
	 */
	adc_init(ADC, sysclk_get_cpu_hz(), 3200000, STARTUP_TIME);
#endif
#endif

	memset((void *)&g_adc_sample_data, 0, sizeof(g_adc_sample_data));

	/* Set ADC timing. */
#if SAM3S ||  SAM3XA || SAM4S
	/* Formula:
	 *     Transfer Time = (TRANSFER * 2 + 3) / ADCClock
	 *     Tracking Time = (TRACKTIM + 1) / ADCClock
	 *     Settling Time = settling value / ADCClock
	 *
	 *     Transfer Time = (1 * 2 + 3) / 6.4MHz = 781 ns
	 *     Tracking Time = (1 + 1) / 6.4MHz = 312 ns
	 *     Settling Time = 3 / 6.4MHz = 469 ns
	 */
	adc_configure_timing(ADC, TRACKING_TIME, ADC_SETTLING_TIME_3, TRANSFER_PERIOD);
#elif  SAM3N || SAM4C
	adc_configure_timing(ADC, TRACKING_TIME);
#elif SAM3U
	/* Formula:
	 *     Sample & Hold Time = SHTIM/ADCClock
	 *
	 *     Sample & Hold Time = 6 / 6.4 = 938 ns
	 */
#ifdef ADC_12B
	adc12b_configure_timing(ADC12B, SAMPLE_HOLD_TIME);
#else
	adc_configure_timing(ADC, SAMPLE_HOLD_TIME);
#endif
#endif

#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
	/* Enable channel number tag. */
	adc_enable_tag(ADC);
	/* Enable/disable sequencer. */
	if (g_adc_test_mode.uc_sequence_en) {
		/* Set user defined channel sequence. */
		adc_configure_sequence(ADC, ch_list, 2);

		/* Enable sequencer. */
		adc_start_sequencer(ADC);

		/* Enable channels. */
		for (i = 0; i < 2; i++) {
			adc_enable_channel(ADC, (enum adc_channel_num_t)i);
		}
		/* Update channel number. */
		g_adc_sample_data.uc_ch_num[0] = ch_list[0];
		g_adc_sample_data.uc_ch_num[1] = ch_list[1];
	} else {
		/* Disable sequencer. */
		adc_stop_sequencer(ADC);

		/* Enable channels. */
		adc_enable_channel(ADC, ADC_CHANNEL_POTENTIOMETER);
#if SAM3S || SAM3XA || SAM4S || SAM4C
		adc_enable_channel(ADC, ADC_TEMPERATURE_SENSOR);
#endif
		/* Update channel number. */
		g_adc_sample_data.uc_ch_num[0] = ADC_CHANNEL_POTENTIOMETER;
#if SAM3S || SAM3XA || SAM4S || SAM4C
		g_adc_sample_data.uc_ch_num[1] = ADC_TEMPERATURE_SENSOR;
#else
		g_adc_sample_data.uc_ch_num[1] = ADC_CHANNEL_POTENTIOMETER;
#endif
	}
#elif SAM3U
#ifdef ADC_12B
	adc12b_enable_channel(ADC12B, ADC_CHANNEL_POTENTIOMETER);
#else
	adc_enable_channel(ADC, ADC_CHANNEL_POTENTIOMETER);
#endif
	g_adc_sample_data.uc_ch_num[0] = ADC_CHANNEL_POTENTIOMETER;
	g_adc_sample_data.uc_ch_num[1] = ADC_CHANNEL_POTENTIOMETER;
#endif

#if SAM3S ||  SAM3XA || SAM4S || SAM4C
	/* Enable the temperature sensor. */
	adc_enable_ts(ADC);
#endif
	/* Set gain and offset (only single ended mode used here). */
#if SAM3S || SAM3XA || SAM4S
	adc_disable_anch(ADC); /* Disable analog change. */
#endif
	if (g_adc_test_mode.uc_gain_en) {
#if SAM3S || SAM3XA || SAM4S
		adc_enable_anch(ADC);
		/* gain = 2 */
		adc_set_channel_input_gain(ADC, ADC_CHANNEL_POTENTIOMETER, ADC_GAINVALUE_2);
#elif SAM3U
#ifdef ADC_12B
		adc12b_set_input_gain(ADC12B, ADC_GAINVALUE_2);
#endif
#endif
	} else {
#if SAM3S || SAM3XA || SAM4S
		/* gain = 1 */
		adc_set_channel_input_gain(ADC, ADC_CHANNEL_POTENTIOMETER, ADC_GAINVALUE_0);
#elif SAM3U
#ifdef ADC_12B
		adc12b_set_input_gain(ADC12B, ADC_GAINVALUE_0);
#endif
#endif
	}

	if (g_adc_test_mode.uc_offset_en) {
#if SAM3S || SAM3XA || SAM4S
		adc_enable_anch(ADC);
		adc_enable_channel_input_offset(ADC, ADC_CHANNEL_POTENTIOMETER);
#elif 	SAM3U
#ifdef ADC_12B
		adc12b_enable_input_offset(ADC12B);
#endif
#endif
	} else {
#if SAM3S || SAM3XA || SAM4S
		adc_disable_channel_input_offset(ADC, ADC_CHANNEL_POTENTIOMETER);
#elif 	SAM3U
#ifdef ADC_12B
		adc12b_disable_input_offset(ADC12B);
#endif
#endif
	}
	/* Set Auto Calibration Mode. */
#if  SAM3S8 || SAM3SD8 || SAM4S
	if (g_adc_test_mode.uc_auto_calib_en) {
		adc_set_calibmode(ADC);
		while (1) {
			if ((adc_get_status(ADC) & ADC_ISR_EOCAL) ==
					ADC_ISR_EOCAL)
				break;
		}
	}
#endif

#if SAM3S8 || SAM4S || SAM3N || SAM3SD8
	/* Set power save. */
	if (g_adc_test_mode.uc_power_save_en) {
		adc_configure_power_save(ADC, 1, 0);
	} else {
		adc_configure_power_save(ADC, 0, 0);;
	}
#elif SAM3U || SAM4C
#ifdef ADC_12B
	/* Set power save. */
	if (g_adc_test_mode.uc_power_save_en) {
		adc12b_configure_power_save(ADC12B, 1, 0);
	} else {
		adc12b_configure_power_save(ADC12B, 0, 0);;
	}

#else
	/* Set power save. */
	if (g_adc_test_mode.uc_power_save_en) {
		adc_configure_power_save(ADC, 1);
	} else {
		adc_configure_power_save(ADC, 0);;
	}
#endif
#endif

#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
	/* Transfer with/without PDC. */
	if (g_adc_test_mode.uc_pdc_en) {
		adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE);
		/* Enable PDC channel interrupt. */
		adc_enable_interrupt(ADC, ADC_IER_RXBUFF);
	} else {
		/* Enable Data ready interrupt. */
		adc_enable_interrupt(ADC, ADC_IER_DRDY);
	}
	/* Enable ADC interrupt. */
	NVIC_EnableIRQ(ADC_IRQn);
#elif SAM3U
#ifdef ADC_12B
	/* Transfer with/without PDC. */
	if (g_adc_test_mode.uc_pdc_en) {
		adc12_read_buffer(ADC12B, g_adc_sample_data.us_value, BUFFER_SIZE);
		/* Enable PDC channel interrupt. */
		adc12b_enable_interrupt(ADC12B, ADC12B_IER_RXBUFF);
	} else {
		/* Enable Data ready interrupt. */
		adc12b_enable_interrupt(ADC12B, ADC12B_IER_DRDY);

	}
	/* Enable ADC interrupt. */
	NVIC_EnableIRQ(ADC12B_IRQn);
#else
	/* Transfer with/without PDC. */
	if (g_adc_test_mode.uc_pdc_en) {
		adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE);
		/* Enable PDC channel interrupt. */
		adc_enable_interrupt(ADC, ADC_IER_RXBUFF);
	} else {
		/* Enable Data ready interrupt. */
		adc_enable_interrupt(ADC, ADC_IER_DRDY);

	}
	/* Enable ADC interrupt. */
	NVIC_EnableIRQ(ADC_IRQn);
#endif
#endif
	/* Configure trigger mode and start convention. */
	switch (g_adc_test_mode.uc_trigger_mode) {
	case TRIGGER_MODE_SOFTWARE:
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
		adc_configure_trigger(ADC, ADC_TRIG_SW, 0);	/* Disable hardware trigger. */
#elif SAM3U
#ifdef ADC_12B
		adc12b_configure_trigger(ADC12B, ADC12B_TRIG_SW);
#else
		adc_configure_trigger(ADC, ADC_TRIG_SW);
#endif
#endif
		break;

	case TRIGGER_MODE_ADTRG:
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
		gpio_configure_pin(PINS_ADC_TRIG, PINS_ADC_TRIG_FLAG);
		adc_configure_trigger(ADC, ADC_TRIG_EXT, 0);
#elif SAM3U
#ifdef ADC_12B
		gpio_configure_pin(PINS_ADC12B_TRIG, PINS_ADC12B_TRIG_FLAG);
		adc12b_configure_trigger(ADC12B, ADC12B_TRIG_EXT);
#else
		gpio_configure_pin(PINS_ADC_TRIG, PINS_ADC_TRIG_FLAG);
		adc_configure_trigger(ADC, ADC_TRIG_EXT);
#endif
#endif
		break;

	case TRIGGER_MODE_TIMER:
		configure_time_trigger();
		break;
#if SAM3S || SAM3U || SAM3XA || SAM4S
	case TRIGGER_MODE_PWM:
		configure_pwm_trigger();
		break;
#endif
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
	case TRIGGER_MODE_FREERUN:
		adc_configure_trigger(ADC, ADC_TRIG_SW, 1);
		break;
#endif
	default:
		break;
	}
}

/**
 * \brief Systick handler.
 */
void SysTick_Handler(void)
{
	gs_ul_ms_ticks++;
}

#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
/**
 * \brief Interrupt handler for the ADC.
 */
void ADC_Handler(void)
{
	uint32_t i;
	uint32_t ul_temp;
	uint8_t uc_ch_num;

	/* With PDC transfer */
	if (g_adc_test_mode.uc_pdc_en) {
		if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==
				ADC_ISR_RXBUFF) {
			g_adc_sample_data.us_done = ADC_DONE_MASK;
			adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE);
			/* Only keep sample value, and discard channel number. */
			for (i = 0; i < NUM_CHANNELS; i++) {
				g_adc_sample_data.us_value[i] &= ADC_LCDR_LDATA_Msk;
			}
		}
	} else {	/* Without PDC transfer */
		if ((adc_get_status(ADC) & ADC_ISR_DRDY) ==
				ADC_ISR_DRDY) {
			ul_temp = adc_get_latest_value(ADC);
			for (i = 0; i < NUM_CHANNELS; i++) {
				uc_ch_num = (ul_temp & ADC_LCDR_CHNB_Msk) >>
						ADC_LCDR_CHNB_Pos;
				if (g_adc_sample_data.uc_ch_num[i] == uc_ch_num) {
					g_adc_sample_data.us_value[i] =
							ul_temp &
							ADC_LCDR_LDATA_Msk;
					g_adc_sample_data.us_done |= 1 << i;
				}
			}
		}
	}
}
Ejemplo n.º 9
0
/**
 *  \brief adc12 Application entry point.
 *
 *  \return Unused (ANSI-C compatibility).
 */
int main(void)
{
	uint32_t i;
	uint8_t uc_key;
	/* Initialize the SAM system. */
	sysclk_init();
	board_init();

	configure_console();

	/* Output example information. */
	puts(STRING_HEADER);

	puts("Configure system tick to get 1ms tick period.\r");
	if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
		puts("-F- Systick configuration error\r");
		while (1);
	}

	/* Set default ADC test mode. */
	memset((void *)&g_adc_test_mode, 0, sizeof(g_adc_test_mode));
	g_adc_test_mode.uc_trigger_mode = TRIGGER_MODE_SOFTWARE;
	g_adc_test_mode.uc_pdc_en = 1;
	g_adc_test_mode.uc_sequence_en = 0;
	g_adc_test_mode.uc_gain_en = 0;
	g_adc_test_mode.uc_offset_en = 0;

	display_menu();
	start_adc();

	puts("Press any key to display configuration menu.\r");
	while (1) {
		/* ADC software trigger per 1s */
		if (g_adc_test_mode.uc_trigger_mode == TRIGGER_MODE_SOFTWARE) {
			mdelay(1000);
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
			adc_start(ADC);
#elif SAM3U
#ifdef ADC_12B
			adc12b_start(ADC12B);
#else
			adc_start(ADC);
#endif
#endif
		}

		/* Check if the user enters a key. */
		if (!uart_read(CONSOLE_UART, &uc_key)) {
#if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C
			adc_disable_interrupt(ADC, 0xFFFFFFFF);	/* Disable all adc interrupt. */
#elif SAM3U
#ifdef ADC_12B
			adc12b_disable_interrupt(ADC12B, 0xFFFFFFFF);	/* Disable all adc interrupt. */
#else
			adc_disable_interrupt(ADC, 0xFFFFFFFF);	/* Disable all adc interrupt. */
#endif
#endif
			tc_start(TC0, 0);	/* Stop the Timer. */
#if SAM3S || SAM3U || SAM3XA || SAM4S
			pwm_channel_disable(PWM, 0);
#endif
			display_menu();
			set_adc_test_mode();
			start_adc();
			puts("Press any key to display configuration menu.\r");
		}

		/* Check if ADC sample is done. */
		if (g_adc_sample_data.us_done == ADC_DONE_MASK) {
			for (i = 0; i < NUM_CHANNELS; i++) {
				printf("CH%02d: %04d mv.    ",
						(int)g_adc_sample_data.uc_ch_num[i],
						(int)(g_adc_sample_data.
								us_value[i] *
								VOLT_REF /
								MAX_DIGITAL));
			}
			puts("\r");
			g_adc_sample_data.us_done = 0;
		}
	}
}
Ejemplo n.º 10
0
/**
 * \brief Application entry point for PWM PDC example.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{

	/* Initialize the SAM system */
	sysclk_init();
	board_init();

	/* Configure the console uart for debug infomation */
	configure_console();

	/* Output example information */
	puts(STRING_HEADER);

/* Enable PWM peripheral clock */
	pmc_enable_periph_clk(ID_PWM);
	
	/*A Disable PWM channels
	- Register: PWM_DIS (PWM Disable)
	- Kanal 0: Motor1_X
	- Kanal 1: Motor1_Y	
	- Kanal 2: Motor1_Z
	- Kanal 3: Referenzsignals 
	*/
	pwm_channel_disable(PWM, PWM_CHANNEL_0);
	pwm_channel_disable(PWM, PWM_CHANNEL_1);
	pwm_channel_disable(PWM, PWM_CHANNEL_2);
	pwm_channel_disable(PWM, PWM_CHANNEL_3);
	
	/*A PWM-Leitungen (C.2 - C.9) im Prozessor vom
	PIO-Controller trennen und auf peripheral Funktion B setzen */

	for (int pinId = 2; pinId <= 9; pinId++) { 
		pio_set_peripheral(PIOC, PIO_TYPE_PIO_PERIPH_B, (1u << pinId));
	}
	
	/*A Clock einstellen
	- Set PWM clock A for all channels, clock B not used
	 */
	pwm_clock_t clock_setting = {
			.ul_clka = PWM_FREQUENCY * PERIOD_VALUE,
			.ul_clkb = 0,
			.ul_mck = sysclk_get_cpu_hz() // diese Funktion gibt 84 Mhz zurück!
		};
	pwm_init(PWM, &clock_setting);
	
	/*A Kanäle 0,1,2 als synchron festlegen
	*/
	// zunächst die generellen Eigenschaften der synchronen Channels initialisieren 
	pwm_channel_t sync_channel = {
		/* die Motor Kanäle sollen alle Center aligned sein */
		.alignment = PWM_ALIGN_CENTER,
		/* die Motor Kanäle sollen mit Low Polarität starten */
		.polarity = PWM_LOW,
		/* Alle Motor Kanäle sollen Clock A verwenden, da sie mit doppelter Basisfrequenz getaktet werden müssen wegen Center aligned */
		.ul_prescaler = PWM_CMR_CPRE_CLKA,
		/* Periode einstellen */
		.ul_period = PERIOD_VALUE,
		/* Duty cycle initial setzen */
		.ul_duty = INIT_DUTY_VALUE,
		/* der channel soll synchron sein */
		.b_sync_ch = true,
		 
		 .b_deadtime_generator = true,
		 
		 .us_deadtime_pwmh = 1,
		 
		 .us_deadtime_pwml = 1 
	};

	/* als erstes dann den Channel 0 initialisieren, indem nur das Channel Attribut in der Struktur von oben neu gesetzt wird,
	der REst bleibt gleich...
	*/
	sync_channel.channel = PWM_CHANNEL_0;
	pwm_channel_init(PWM, &sync_channel);
	// das gleiche dann mit den beiden anderen zu synchronisierenden Channels
	sync_channel.channel = PWM_CHANNEL_1;
	pwm_channel_init(PWM, &sync_channel);
	sync_channel.channel = PWM_CHANNEL_2;
	pwm_channel_init(PWM, &sync_channel);

	/*
	 * Initialize PWM synchronous channels
	 * Synchronous Update Mode: Automatic update duty cycle value by the PDC
	 * and automatic update of synchronous channels. The update occurs when
	 * the Update Period elapses (MODE 2 --> Vorsicht vor Verwirrung: dies entspricht der "Methode 3" aus dem Datasheet!).
	 * Synchronous Update Period = MAX_SYNC_UPDATE_PERIOD.
	 */
	pwm_sync_init(PWM, PWM_SYNC_UPDATE_MODE_2, MAX_SYNC_UPDATE_PERIOD);

	/*
	 * Request PDC transfer as soon as the synchronous update period elapses
	 */
	pwm_pdc_set_request_mode(PWM, PWM_PDC_UPDATE_PERIOD_ELAPSED, (1 << 0));
	
	/* Aktiviere alle synchronen channel durch aktivieren von channel 0, channel 1 und 2 werden automatisch synchron mit gestartet 
	zusätzlich noch den Refernzchannel "synchron" mitstarten
	die ASF funktion channel_enable erlaubt leider nur die Übergabe eines Kanals, also müssen wir hier wohl mit direktem Zugriff auf das Register arbeiten */
	pwm_channel_enable(PWM, PWM_CHANNEL_0);
  // ref channel funktioniert noch nicht, muss noch debugged werden  pwm_channel_enable(PWM, PWM_CHANNEL_3);
  
  pmc_enable_periph_clk(ID_PIOA);
  pio_set_output(PIOA, PIO_PA23, LOW, DISABLE, ENABLE);
  
  int angle = 0;
  int up = 1;
  float sinus = 0;
  float a;
  
	while (1) {
		
		pio_set(PIOA, PIO_PA23);
		for (int i = 0; i < 1000; i++)
		{
			sinus = sin(0.343543);
			a = sinus;
		}
		
		pio_clear(PIOA, PIO_PA23);
		delay_us(4);
		
		
		/*display_menu();
		uint32_t angle = get_num_value();
		struct SV pwm = get_vector_for_angle(angle);
		SVPWM(pwm.u, pwm.v, pwm.w);
		printf("The angle is %i degrees.", angle);
		
		for (int i = 0; i <= 360; i++) {
			struct SV pwm = get_vector_for_angle(i);
			SVPWM(pwm.u, pwm.v, pwm.w);
			printf("%i Grad\r\n", i);
			delay_ms(40);
			
		}
		for (int i = 360; i >= 0; i--) {
			struct SV pwm = get_vector_for_angle(i);
			SVPWM(pwm.u, pwm.v, pwm.w);
			printf("%i Grad\r\n", i);
			delay_ms(40);
			
		}*/
	}
}
Ejemplo n.º 11
0
/**
 * \brief Application entry point for PWM with LED example.
 * Output PWM waves on LEDs to make them fade in and out.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{
	/* Initialize the SAM system */
	sysclk_init();
	board_init();

	/* Configure the console uart for debug information */
	configure_console();

	/* Output example information */
	puts(STRING_HEADER);
	
	/* Enable PWM peripheral clock */
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	pmc_enable_periph_clk(ID_PWM0);
#else
	pmc_enable_periph_clk(ID_PWM);
#endif

	/* Disable PWM channels for LEDs */
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	pwm_channel_disable(PWM0, PIN_PWM_LED0_CHANNEL);
	pwm_channel_disable(PWM0, PIN_PWM_LED1_CHANNEL);
#else
	pwm_channel_disable(PWM, PIN_PWM_LED0_CHANNEL);
	pwm_channel_disable(PWM, PIN_PWM_LED1_CHANNEL);
#endif

	/* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */
	pwm_clock_t clock_setting = {
		.ul_clka = PWM_FREQUENCY * PERIOD_VALUE,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	pwm_init(PWM0, &clock_setting);
#else
	pwm_init(PWM, &clock_setting);
#endif

	/* Initialize PWM channel for LED0 */
	/* Period is left-aligned */
	g_pwm_channel_led.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel_led.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel_led.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_led.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_led.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_led.channel = PIN_PWM_LED0_CHANNEL;
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	pwm_channel_init(PWM0, &g_pwm_channel_led);
#else
	pwm_channel_init(PWM, &g_pwm_channel_led);
#endif

	/* Enable channel counter event interrupt */
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	pwm_channel_enable_interrupt(PWM0, PIN_PWM_LED0_CHANNEL, 0);
#else
	pwm_channel_enable_interrupt(PWM, PIN_PWM_LED0_CHANNEL, 0);
#endif

	/* Initialize PWM channel for LED1 */
	/* Period is center-aligned */
	g_pwm_channel_led.alignment = PWM_ALIGN_CENTER;
	/* Output waveform starts at a high level */
	g_pwm_channel_led.polarity = PWM_HIGH;
	/* Use PWM clock A as source clock */
	g_pwm_channel_led.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_led.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_led.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_led.channel = PIN_PWM_LED1_CHANNEL;
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	pwm_channel_init(PWM0, &g_pwm_channel_led);

	/* Disable channel counter event interrupt */
	pwm_channel_disable_interrupt(PWM0, PIN_PWM_LED1_CHANNEL, 0);
#else
	pwm_channel_init(PWM, &g_pwm_channel_led);

	/* Disable channel counter event interrupt */
	pwm_channel_disable_interrupt(PWM, PIN_PWM_LED1_CHANNEL, 0);
#endif

	/* Configure interrupt and enable PWM interrupt */
#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
	NVIC_DisableIRQ(PWM0_IRQn);
	NVIC_ClearPendingIRQ(PWM0_IRQn);
	NVIC_SetPriority(PWM0_IRQn, 0);
	NVIC_EnableIRQ(PWM0_IRQn);
	
	/* Enable PWM channels for LEDs */
	pwm_channel_enable(PWM0, PIN_PWM_LED0_CHANNEL);
	pwm_channel_enable(PWM0, PIN_PWM_LED1_CHANNEL);
#else
	NVIC_DisableIRQ(PWM_IRQn);
	NVIC_ClearPendingIRQ(PWM_IRQn);
	NVIC_SetPriority(PWM_IRQn, 0);
	NVIC_EnableIRQ(PWM_IRQn);
	
	/* Enable PWM channels for LEDs */
	pwm_channel_enable(PWM, PIN_PWM_LED0_CHANNEL);
	pwm_channel_enable(PWM, PIN_PWM_LED1_CHANNEL);
#endif


	/* Infinite loop */
	while (1) {
	}
}
Ejemplo n.º 12
0
static void configure_time_trigger_for_ssc(uint32_t ssc_trigger_hz)
{
	
#if 0
	gpio_configure_pin(PIO_PA12_IDX, PIO_PERIPH_B);
	pmc_enable_periph_clk(ID_PWM);

	/* Disable PWM channels */
	pwm_channel_disable(PWM, PWM_CHANNEL_1);

	/* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */
	pwm_clock_t clock_setting = {
		.ul_clka = 1000 * 100,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM, &clock_setting);
	
	pwm_channel_t g_pwm_channel;

	/* Period is left-aligned */
	g_pwm_channel.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel.ul_period = 100;
	/* Duty cycle value of output waveform */
	g_pwm_channel.ul_duty = 50;
	g_pwm_channel.channel = PWM_CHANNEL_1;
	pwm_channel_init(PWM, &g_pwm_channel);
	
	/* Disable channel counter event interrupt */
	pwm_channel_disable_interrupt(PWM, PWM_CHANNEL_1, 0);
	
	pwm_channel_enable(PWM, PWM_CHANNEL_1);
#endif


	
	uint32_t ul_div = 0;
	uint32_t ul_tc_clks = 0;
	uint32_t ul_sysclk = sysclk_get_cpu_hz();

	pmc_set_writeprotect(false);

	// Enable peripheral clock.
	pmc_enable_periph_clk(CONF_SSC_CLOCK_SOURCE_ID);

	// TIOA configuration 
	// gpio_configure_pin(PIN_TC0_TIOA0, PIN_TC0_TIOA0_FLAGS);
	// tc_set_writeprotect(TC2, true);
	tc_set_writeprotect(CONF_SSC_CLOCK_TC, false);

	// Configure TC for a 1Hz frequency and trigger on RC compare.
	tc_find_mck_divisor(ssc_trigger_hz, ul_sysclk, &ul_div, &ul_tc_clks, ul_sysclk);
	tc_init(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, ul_tc_clks | TC_CMR_WAVSEL_UP_RC | TC_CMR_WAVE | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
	uint32_t tmp_val = (ul_sysclk / ul_div) / ssc_trigger_hz;
	
	tc_write_ra(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, tmp_val / 2);
	tc_write_rc(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, tmp_val);

	// Start the Timer.
	tc_start(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL);

}

void tm_stick_init(uint32_t bus_speed_hz) {
	if(g_tm_stick_data.mutex == NULL) {
		g_tm_stick_data.mutex =  xSemaphoreCreateMutex();
	}
	
	if(g_tm_stick_data.rtos_task_semaphore == NULL) {
		vSemaphoreCreateBinary(g_tm_stick_data.rtos_task_semaphore);
	}
	
	configure_time_trigger_for_ssc(1000);
	
	sysclk_enable_peripheral_clock(ID_SSC);
	ssc_reset(SSC);
	// Do not go over 1MHz, the pulse will be too long and extend over to the next clock's rising edge (The spec. sheet of 4021BCM does not really list the operation times of 3.3V operation.
	// I tested a couple of speeds.... at 1.25MHz, I found that it sometimes misses some pulses.
	if(bus_speed_hz > TM_STICK_MAX_BUS_SPEED){
		bus_speed_hz = TM_STICK_MAX_BUS_SPEED;
	}
	ssc_set_clock_divider(SSC, bus_speed_hz, sysclk_get_cpu_hz()); 
	clock_opt_t rx_clk_opt = {
		.ul_cks = SSC_RCMR_CKS_MCK,
		.ul_cko = SSC_RCMR_CKO_TRANSFER,
// TODO: Fix the SSC clock for some reason shift 1/2 clock pulse position.
// This makes the button results shift one position to the right. So we have to change rising/falling edge to "correct" it (oh, well, "hack" around it).
// Don't know why.
// One possible patch up is to use a pin to signal which one is desired to determine which of the following settings to use..
#if defined(CONF_BOARD_ARDUINO_DUE) 
		.ul_cki = 0, //SSC_RCMR_CKI,
#else
		.ul_cki = SSC_RCMR_CKI,
#endif
		.ul_ckg = SSC_RCMR_CKG_CONTINUOUS,
		.ul_start_sel = SSC_RCMR_START_RF_FALLING,
		.ul_period = 0,
		.ul_sttdly = 0
	};
	
	data_frame_opt_t rx_data_frame_opt = {
		.ul_datlen = 23,
		.ul_msbf = 0,  //SSC_RFMR_MSBF,
		.ul_fsos = SSC_RFMR_FSOS_NONE,
		.ul_datnb = 0,
		.ul_fsedge = SSC_RFMR_FSEDGE_NEGATIVE,
		.ul_fslen = 0,
		.ul_fslen_ext = 0
	};
	
	ssc_set_receiver(SSC, &rx_clk_opt, &rx_data_frame_opt);
	ssc_enable_interrupt(SSC, SSC_IER_RXRDY);
	
	// It is VERY IMPORTANT to set the priority of the interrupt to conform to what FreeRTOS needs because we are calling FreeRTOS interrupt-safe APIs (those *FromISR) from within interrupt handlers.
	// If we don't do this, if would work at the beginning, and then eventually the whole thing will come crashing down.
	// And it's not gonna crash even after millions of interrupts are handled. It will come crashing down in some very weird place after you start using another interrupt handler, like the pio handlers, and only after some handlling...
	// And, it will appear random. You press the button a couple of times, it dies. Then, at other times, you press and hold the button, etc. etc. Each time will be different.
	// You would be suspecting your stack overflowed, your code does a wild pointer, etc. etc. It's very difficult to debug. Trust me, you don't wanna go there.
	NVIC_ClearPendingIRQ(SSC_IRQn);
	NVIC_SetPriority(SSC_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
	
	NVIC_EnableIRQ(SSC_IRQn); 
	
	ssc_enable_rx(SSC);
}

void SSC_Handler( void ) {	
	portBASE_TYPE xHigherTaskWoken = pdFALSE;
	uint32_t* in_data_buf = (uint32_t*)g_tm_stick_data.data;
	uint32_t in_data = 0;
	static uint32_t previous_in_data = 0;
	static bool is_first_time = true;
	// calculate the mask needed to wipe off extra high bit junk.
	static uint32_t mask = 0xFFFFFFFF;
	if(is_first_time) {
		for(int i = 4; i > TM_STICK_NUM_DATA_BYTES; i--) {
			mask = mask >> 8;
		}
	}

	if(ssc_is_rx_ready(SSC) == SSC_RC_YES) {
		xSemaphoreTakeFromISR(g_tm_stick_data.mutex, &xHigherTaskWoken);
		in_data = SSC->SSC_RHR;
		// in_data >>= 1; // No idea why it always reads 25bits (one too many bit at the end LSB), instead of 24 I told it to. Therefore, we shift it off.
		in_data = ~in_data; // reverse it. So, now 1 is on, 0 is off.
		in_data &= mask;
		
		// Glitch filtering below.
		if(is_first_time
			||  (previous_in_data ^ in_data) == 0) {
			is_first_time = false;
			*in_data_buf = in_data;
		}
		previous_in_data = in_data;
		xSemaphoreGiveFromISR(g_tm_stick_data.mutex, &xHigherTaskWoken);
	
		xHigherTaskWoken = pdFALSE;
		xSemaphoreGiveFromISR(g_tm_stick_data.rtos_task_semaphore, &xHigherTaskWoken); // if there is an RTOS waiting for the ADC task mutex, wake it up.
		portEND_SWITCHING_ISR(xHigherTaskWoken);
	}
}

#ifdef __cplusplus
}
void init_pwm(void)
{
	/* Enable PWM peripheral clock */
	pmc_enable_periph_clk(ID_PWM0);

	/* Disable PWM channels for LEDs */
	pwm_channel_disable(PWM0, PIN_PWM_LED0_CHANNEL);
	pwm_channel_disable(PWM0, PIN_PWM_LED1_CHANNEL);

	/* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */
	pwm_clock_t clock_setting = {
		.ul_clka = PWM_FREQUENCY * PERIOD_VALUE,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM0, &clock_setting);

	/* Initialize PWM channel for LED0 */
	/* Period is left-aligned */
	g_pwm_channel_led.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel_led.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel_led.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_led.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_led.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_led.channel = PIN_PWM_LED0_CHANNEL;
	pwm_channel_init(PWM0, &g_pwm_channel_led);

	/* Enable channel counter event interrupt */
	pwm_channel_enable_interrupt(PWM0, PIN_PWM_LED0_CHANNEL, 0);

	/* Initialize PWM channel for LED1 */
	/* Period is center-aligned */
	g_pwm_channel_led.alignment = PWM_ALIGN_CENTER;
	/* Output waveform starts at a high level */
	g_pwm_channel_led.polarity = PWM_HIGH;
	/* Use PWM clock A as source clock */
	g_pwm_channel_led.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel_led.ul_period = PERIOD_VALUE;
	/* Duty cycle value of output waveform */
	g_pwm_channel_led.ul_duty = INIT_DUTY_VALUE;
	g_pwm_channel_led.channel = PIN_PWM_LED1_CHANNEL;
	pwm_channel_init(PWM0, &g_pwm_channel_led);

	/* Disable channel counter event interrupt */
	pwm_channel_disable_interrupt(PWM0, PIN_PWM_LED1_CHANNEL, 0);

	/* Configure interrupt and enable PWM interrupt */
	NVIC_DisableIRQ(PWM0_IRQn);
	NVIC_ClearPendingIRQ(PWM0_IRQn);
	NVIC_SetPriority(PWM0_IRQn, 0);
	NVIC_EnableIRQ(PWM0_IRQn);
	
	/* Enable PWM channels for LEDs */
	pwm_channel_enable(PWM0, PIN_PWM_LED0_CHANNEL);
//jsi 15feb16	pwm_channel_enable(PWM0, PIN_PWM_LED1_CHANNEL);

}
/**
 *  \brief Handler for System Tick interrupt.
 *
 *  Process System Tick Event.
 *  Increment the ul_ms_ticks counter.
 */
void SysTick_Handler(void)
{
	g_ul_tick_count++;
	ul_ms_ticks++; //jsi 6feb16
	
	timerTickCount++;
	timerTickCount &= MAX_TICK; // force rollover at this count to avoid confusion detecting rollover with the MSbit set
	if (timerTickCount == 0)
	{
		rollover = 1;
	}
	
	if ((timerTickCount % TICKS_PER_SEC) == 0)
	{
		process_timers();
	}

	
	if (controls.buzzer_enable)
	{
		if (controls.buzzer_cycle == CYCLE_ON)
		{
			if (controls.buzzer_dur_count++ > controls.buzzer_on_dur)
			{
				if (controls.buzzer_repeat_count++ >= controls.buzzer_repeat)
				{
					controls.buzzer_enable = 0;
					pwm_channel_disable(PWM0, PIN_PWM_LED0_CHANNEL);
				}
				else
				{
					controls.buzzer_dur_count = 0;
					controls.buzzer_cycle = CYCLE_OFF;
					pwm_channel_disable(PWM0, PIN_PWM_LED0_CHANNEL);
				}
			}
		}
		else
		{
			if (controls.buzzer_dur_count++ > controls.buzzer_off_dur)
			{
				controls.buzzer_dur_count = 0;
				controls.buzzer_cycle = CYCLE_ON;
				pwm_channel_enable(PWM0, PIN_PWM_LED0_CHANNEL);
			}
		}
	}
	
	if (controls.solenoid_enable)
	{
		controls.solenoid_count++;
		
		if (controls.solenoid_cycle == CYCLE_ON)
		{
			if (controls.solenoid_count > SOLENOID_ON_COUNT)
			{
				controls.solenoid_count = 0;
				controls.solenoid_cycle = CYCLE_OFF;
				controls.solenoid_enable = 0; //solenoid is a one-shot
				ioport_set_pin_level(ECLAVE_SOLENOID, IOPORT_PIN_LEVEL_LOW);
			}
		}
		else
		{
			if (controls.solenoid_count > SOLENOID_OFF_COUNT)
			{
				controls.solenoid_count = 0;
				controls.solenoid_cycle = CYCLE_ON;
				ioport_set_pin_level(ECLAVE_SOLENOID, IOPORT_PIN_LEVEL_HIGH);
			}
			
		}
	}
}