dig:mic-servo
Here are the main.c and mic-servo.py code for a working project for lab_3. You can download the AVR32 project (the device side) as mic-servo.zip.
main.c
////////////////////////////////////////////////////////////////////////////// // RGG LED main.c ////////////////////////////////////////////////////////////////////////////// #include "compiler.h" #include "preprocessor.h" #include "pm.h" #include "gpio.h" #include "pwm.h" #include "adc.h" #include "dsp.h" #include "tc.h" //#include "print_funcs.h" #include "intc.h" #include "power_clocks_lib.h" #include "conf_usb.h" #include "usb_task.h" #if USB_DEVICE_FEATURE == ENABLED #include "usb_drv.h" #include "usb_descriptors.h" #include "usb_standard_request.h" #include "device_task.h" #endif // RGB LED and servo PWM defines #define cR 0 #define cG 1 #define cB 2 #define cServo 3 // indexes into channel arrays #define R_PWM_PIN AVR32_PWM_1_0_PIN // PA08 #define R_PWM_FUNCTION AVR32_PWM_1_0_FUNCTION #define R_PWM_CHANNEL_ID 1 // note we skip PWM[2]=pin 25 because it is used by default fuse settings and DFU bootloader code as DFU launcher input pin #define G_PWM_PIN AVR32_PWM_3_0_PIN // PA14 #define G_PWM_FUNCTION AVR32_PWM_3_0_FUNCTION #define G_PWM_CHANNEL_ID 3 #define B_PWM_PIN AVR32_PWM_4_0_PIN // PA15 #define B_PWM_FUNCTION AVR32_PWM_4_0_FUNCTION #define B_PWM_CHANNEL_ID 4 static pwm_opt_t pwm_opt; static avr32_pwm_channel_t pwm_channel[4]; static unsigned int channel_id[4]; #define TTT 4 // defines how to multiply PWM period and duty cycle values for the RGB LED PWM channels. 4 gives 4kHz frequency. // Timer / Counter, used to generate interrupt for ADC sampling # define EXAMPLE_TC (&AVR32_TC) # define EXAMPLE_TC_IRQ_GROUP AVR32_TC_IRQ_GROUP # define EXAMPLE_TC_IRQ AVR32_TC_IRQ0 #define FADC 10000 // desired ADC sample rate in Hz #define TC_CHANNEL 0 volatile U32 tc_tick = 0; // counts samples volatile bool takeSampleNow=TRUE; // ISR sets to tell main loop to sample ADC int dspCounter=0; // used to count signal processing steps for audMean, so that meanSq power estimate filtering can occur at intervals of TAU2 // adc #define ADC_CHANNEL 0 #define ADC_PIN AVR32_ADC_AD_0_PIN // PA03, used by LDR on bronze board or hacked copper board #define ADC_FUNCTION AVR32_ADC_AD_0_FUNCTION // Signal processing: long audMean=0; // holds mean mic signal, for high pass filtering long meanSq=0; // holds mean square audio signal long maxSq=0; // holds max, for clipping detection unsigned int servo; // the computed servo signal, ranging from 1000-2000 us bool initialized=0; // flags if filters have been initialized #define NTAU1 6 // 'time constant' in samples is 2^TAU1 of high pass filter for mean audio level. Actual RC time constant is deltaT*(2^TAU1) e.g. 25ms for TAU1=8 and fsample=10kHz. #define TAU2 64 // 'time constant' in samples of lowpass second filter as multiple of time constant of highpass filter // servo #define FSERVOUPDATE 100 // update rate for servo pulse width in main loop in Hz #define SERVO_PIN AVR32_PWM_6_0_PIN // PWM_6_0 is PA19 #define SERVO_PWM_FUNCTION AVR32_PWM_6_0_FUNCTION #define SERVO_PWM_CHANNEL_ID 6 #define SERVO_MID 773 // counts to get 1500us pulse width #define SERVO_MULT 0.5156 // for static calculations; multiply us by this to get cdty count // USB static U32 sof_cnt; // start-of-frame counts, get one every ms static U8 in_data_length; static U8 in_buf[EP_SIZE_TEMP1]; // 64 byte data buffer for in packet (to host) static U8 out_data_length; static U8 out_buf[EP_SIZE_TEMP2]; // data buffer for out packet, from host ////////////////////////////////////////////////////////////////////////////// // clock setup ////////////////////////////////////////////////////////////////////////////// #define FOSC0 12000000 //!< Osc0 frequency: Hz. #define FPBA (FOSC0*11/2/2) // from power manager clock setup = 33MHz #define OSC0_STARTUP AVR32_PM_OSCCTRL0_STARTUP_2048_RCOSC //!< Osc0 startup time: RCOsc periods. void init_clock() { // source: gpio_local_bus_example.c // Initialize domain clocks (CPU, HSB, PBA and PBB) to the max frequency available // without flash wait states. // Some of the registers in the GPIO module are mapped onto the CPU local bus. // To ensure maximum transfer speed and cycle determinism, any slaves being // addressed by the CPU on the local bus must be able to receive and transmit // data on the bus at CPU clock speeds. The consequences of this is that the // GPIO module has to run at the CPU clock frequency when local bus transfers // are being performed => we want fPBA = fCPU. // Switch the main clock source to Osc0. pm_switch_to_osc0(&AVR32_PM, FOSC0, OSC0_STARTUP); // Setup PLL0 on Osc0, mul=10 ,no divisor, lockcount=16: 12Mhzx11 = 132MHz output pm_pll_setup(&AVR32_PM, 0, // pll. 10, // mul. 1, // div. 0, // osc. 16); // lockcount. // PLL output VCO frequency is 132MHz. // We divide it by 2 with the pll_div2=1 to get a main clock at 66MHz. pm_pll_set_option(&AVR32_PM, 0, // pll. 1, // pll_freq. 1, // pll_div2. 0); // pll_wbwdisable. // Enable the PLL. pm_pll_enable(&AVR32_PM, 0); // Wait until the PLL output is stable. pm_wait_for_pll0_locked(&AVR32_PM); // Configure each clock domain to use the main clock divided by 2 // => fCPU = fPBA = fPBB = 33MHz. pm_cksel(&AVR32_PM, 1, // pbadiv. 0, // pbasel. 1, // pbbdiv. 0, // pbbsel. 1, // hsbdiv=cpudiv 0); // hsbsel=cpusel // Switch the main clock source to PLL0. pm_switch_to_clock(&AVR32_PM, AVR32_PM_MCCTRL_MCSEL_PLL0); } ////////////////////////////////////////////////////////////////////////////// // PWM setup ////////////////////////////////////////////////////////////////////////////// // PWM setup void init_pwm() { // set PWM GPIOs gpio_enable_module_pin(R_PWM_PIN, R_PWM_FUNCTION); gpio_enable_module_pin(G_PWM_PIN, G_PWM_FUNCTION); gpio_enable_module_pin(B_PWM_PIN, B_PWM_FUNCTION); gpio_enable_module_pin(SERVO_PIN, SERVO_PWM_FUNCTION); // PWM controller configuration. pwm_opt.diva = AVR32_PWM_DIVA_CLK_OFF; // no divider pwm_opt.divb = AVR32_PWM_DIVB_CLK_OFF; pwm_opt.prea = AVR32_PWM_PREA_MCK; // no prescaler for div a or b pwm_opt.preb = AVR32_PWM_PREB_MCK; // init pwm globals pwm_init(&pwm_opt); channel_id[cR] = R_PWM_CHANNEL_ID; channel_id[cG] = G_PWM_CHANNEL_ID; channel_id[cB] = B_PWM_CHANNEL_ID; channel_id[cServo] = SERVO_PWM_CHANNEL_ID; // the core runs at 33MHz (see init_clock()). Therefore we get PWM base clock of 33Mhz. // Then we divide this down by powers of 2 and supply 20 bit count values for the period and duty cycle. // We observe that the LED frequency is 4kHz. // The LED channels are set up to use 33 MHz/2 clock source, and count to 256<<4=4096 for the period. // Therefore we expect that the frequency is 33M/2/4k=4.028kHz. This is exactly what we observe. // To get a reasonable servo pulse frequency of 200Hz, we use for the servo channel a further division by 32 to get // a frequency of 4.028kHz/32=125.9Hz. Therefore we use a prescaler of 64 instead of 2. unsigned int c; for (c = cR; c <= cB; c++) { pwm_channel[c].CMR.calg = PWM_MODE_LEFT_ALIGNED; // Channel mode. pwm_channel[c].CMR.cpol = PWM_POLARITY_HIGH; // Channel polarity. pwm_channel[c].CMR.cpd = PWM_UPDATE_DUTY; // Not used the first time. pwm_channel[c].CMR.cpre = AVR32_PWM_CPRE_MCK_DIV_2; // Channel prescaler. pwm_channel[c].cdty = 0; // Channel duty cycle, should be < CPRD. pwm_channel[c].cprd = (256 << TTT); // Channel period. pwm_channel[c].cupd = 0; // Channel update is not used here. pwm_channel_init(channel_id[c], &pwm_channel[c]); } // servo channel // the duty cycle units here are in units of the prescaled clock period which is 64/33MHz=1.939us. // Therefore to get X us we need to supply X/1.939 counts to cdty, or approximately X/2 counts. // Therefore to get 1500us we need to supply 773 counts or about 750 counts. pwm_channel[cServo].CMR.calg = PWM_MODE_LEFT_ALIGNED; // Channel mode. pwm_channel[cServo].CMR.cpol = PWM_POLARITY_HIGH; // Channel polarity. pwm_channel[cServo].CMR.cpd = PWM_UPDATE_DUTY; // Not used the first time. pwm_channel[cServo].CMR.cpre = AVR32_PWM_CPRE_MCK_DIV_64; // Channel prescaler - should give 125.9Hz pwm_channel[cServo].cdty = 0; // start at zero to leave servo disabled if it was disabled and is digital servo with annoying whine. // SERVO_MID; // Channel duty cycle, should be < CPRD. pwm_channel[cServo].cprd = (256 << TTT); // Channel period. Use same as LED so that we get 4kHz/32=125.9Hz frequency. pwm_channel[cServo].cupd = 0; // Channel update is not used here. pwm_channel_init(channel_id[cServo], &pwm_channel[cServo]); pwm_start_channels((1 << channel_id[cR]) | (1 << channel_id[cG]) | (1<< channel_id[cB])|(1<<channel_id[cServo])); } // sets RGB LED brightnesses, r,g,b range from 0-255 void set_rgb(U8 r, U8 g, U8 b) { U8 c; for (c = cR; c <= cB; c++) { // Channel duty cycle, should be < CPRD. switch (c) { case cR: pwm_channel[c].cdty = ((U32) r) << TTT; break; case cG: pwm_channel[c].cdty = ((U32) g) << TTT; break; case cB: pwm_channel[c].cdty = ((U32) b) << TTT; break; } pwm_channel_init(channel_id[c], &pwm_channel[c]); } } /** * Sets the pulse width for the servo output. 1000 to 2000 is servo range. * Argument, pulse width in us from 1000 to 2000 * */ void setServoPWUs(U16 us) { pwm_channel[cServo].cdty = ((U32) us>>1); // TODO almost correct, double quantity to give almost exactly us, see SERVO_MULT and pwm_init() pwm_channel_init(channel_id[cServo], &pwm_channel[cServo]); } ////////////////////////////////////////////////////////////////////////////// // ADC ////////////////////////////////////////////////////////////////////////////// // initializes ADC for one channel void init_adc() { // GPIO pin/adc-function map. static const gpio_map_t ADC_GPIO_MAP = { { ADC_PIN, ADC_FUNCTION } }; volatile avr32_adc_t *adc = &AVR32_ADC; // ADC IP registers address // Assign and enable GPIO pins to the ADC function. gpio_enable_module(ADC_GPIO_MAP, sizeof(ADC_GPIO_MAP) / sizeof(ADC_GPIO_MAP[0])); // configure ADC // Lower the ADC clock to match the ADC characteristics (because we configured // the CPU clock to 33MHz, and the ADC clock requires less than 5 MHz for 10 bit ADC, // therefore prescale by ; // cf. the ADC Characteristic section in the datasheet). // TODO These ADC config numbers are wrong currently! AVR32_ADC.mr |= 0x3 << AVR32_ADC_MR_PRESCAL_OFFSET; adc_configure(adc); // Assign the on-board sensors to their ADC channel. unsigned short adc_channel = ADC_CHANNEL; // Enable the ADC channels. adc_enable(adc, adc_channel); } U16 get_adc_value() { // launch conversion on all enabled channels volatile avr32_adc_t *adc = &AVR32_ADC; // ADC IP registers address adc_start(adc); // get value for the adc channel U16 adc_value = adc_get_value(adc, ADC_CHANNEL); return adc_value; } ////////////////////////////////////////////////////////////////////////////// // USB ////////////////////////////////////////////////////////////////////////////// //! //! @brief This function initializes the hardware/software resources required for device applicative task. //! void device_task_init(void) { sof_cnt = 0; in_data_length = 0; out_data_length = 0; Usb_enable_sof_interrupt();// enables interrupt every ms for start of frame } //! //! @brief Entry point of the device applicative task management //! //! This function links the device application to the USB bus. //! void device_task(void) { if (takeSampleNow) { // flag set in timer ISR gpio_local_tgl_gpio_pin(AVR32_PIN_PA11); // debug takeSampleNow=FALSE; // signal processing S16 adcval = (S16)get_adc_value(); // 0-1023=3.3V if (initialized) audMean = ((adcval-audMean)>>NTAU1)+audMean; // TODO mix old and new value else audMean = adcval; // init filter with first reading if(dspCounter--==0){ // only update meanSq at this interval, so to produce effective time constant that is TAU2 times tau of audMean filtering dspCounter=TAU2; long diff = adcval - audMean; // signed diff of sample from mean long sq = diff * diff; // square diff if (initialized) meanSq = ((sq-meanSq)>>NTAU1)+meanSq; // low pass square diff else meanSq = sq; if (meanSq > maxSq) maxSq = meanSq; // not used now } initialized = 1; // we update the servo at FSERVOUPDATE frequency. Since the FADC is higher, we can count ADC cycles // and update the servo only every FADC/FSERVOUPDATE samples if (tc_tick % (FADC / FSERVOUPDATE) == 0) { servo = (unsigned int) (meanSq >> 3); // send meanSq back to host if (servo > 2000) servo = 2000; else if (servo < 1000) servo = 1000; setServoPWUs(servo); // sets 1000 to 2000 us // only communicate if we are enumerated if (!Is_device_enumerated()) return; // HERE STARTS THE USB DEVICE APPLICATIVE CODE // Load the IN endpoint (to the host PC) with the desired measurement to show on host if (Is_usb_in_ready(EP_TEMP_IN)) { // store bytes of 32 bit value to buffer...: in_buf[0] = 0xFF & (meanSq>>24); // big endian order, MSB goes first in array in_buf[1] = 0xff & (meanSq>>16); in_buf[2] = 0xFF & (meanSq >> 8); in_buf[3] = 0xFF & (meanSq >> 0); in_data_length = 4; // do magic to send the packet Usb_reset_endpoint_fifo_access(EP_TEMP_IN); usb_write_ep_txpacket(EP_TEMP_IN, in_buf, in_data_length, NULL); in_data_length = 0; Usb_ack_in_ready_send(EP_TEMP_IN); } // If we receive something in the OUT endpoint (from the host), use it to set the RGB color if (Is_usb_out_received(EP_TEMP_OUT)) { gpio_local_tgl_gpio_pin(AVR32_PIN_PA12); // debug data from host Usb_reset_endpoint_fifo_access(EP_TEMP_OUT); out_data_length = Usb_byte_count(EP_TEMP_OUT); usb_read_ep_rxpacket(EP_TEMP_OUT, out_buf, out_data_length, NULL); // store the received data in out_buf Usb_ack_out_received_free(EP_TEMP_OUT); // update PWM: set_rgb(out_buf[1], out_buf[2], out_buf[3]); } } } } //! //! @brief usb_sof_action //! //! This function increments the sof_cnt counter each time //! the USB Start-of-Frame interrupt subroutine is executed (1 ms). //! Useful to manage time delays //! void usb_sof_action(void) { // gpio_local_tgl_gpio_pin(AVR32_PIN_PA10); // debug, should toggle every ms sof_cnt++; } /*! \brief TC interrupt - used for AD conversion. */ #if defined (__GNUC__) __attribute__((__interrupt__)) #elif defined (__ICCAVR32__) #pragma handler = EXAMPLE_TC_IRQ_GROUP, 1 __interrupt #endif static void tc_irq(void) { // Increment the counter, which is also used to determine servo updates tc_tick++; // set a flag to tell main loop to take a sample takeSampleNow = TRUE; // Clear the interrupt flag. This is a side effect of reading the TC SR. tc_read_sr(EXAMPLE_TC, TC_CHANNEL); // Toggle a GPIO pin (this pin is used as a regular GPIO pin). gpio_local_tgl_gpio_pin(AVR32_PIN_PA10); // debug, should toggle at desired sample rate } void init_tc(){ // Timer/Counter Options for waveform generation. static const tc_waveform_opt_t WAVEFORM_OPT = { .channel = TC_CHANNEL, // Channel selection. .bswtrg = TC_EVT_EFFECT_NOOP, // Software trigger effect on TIOB. .beevt = TC_EVT_EFFECT_NOOP, // External event effect on TIOB. .bcpc = TC_EVT_EFFECT_NOOP, // RC compare effect on TIOB. .bcpb = TC_EVT_EFFECT_NOOP, // RB compare effect on TIOB. .aswtrg = TC_EVT_EFFECT_NOOP, // Software trigger effect on TIOA. .aeevt = TC_EVT_EFFECT_NOOP, // External event effect on TIOA. .acpc = TC_EVT_EFFECT_NOOP, // RC compare effect on TIOA: toggle. .acpa = TC_EVT_EFFECT_NOOP, // RA compare effect on TIOA: toggle (other possibilities are none, set and clear). .wavsel = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER,// Waveform selection: Up mode with automatic trigger(reset) on RC compare. .enetrg = FALSE, // External event trigger enable. .eevt = 0, // External event selection. .eevtedg = TC_SEL_NO_EDGE, // External event edge selection. .cpcdis = FALSE, // Counter disable when RC compare. .cpcstop = FALSE, // Counter clock stopped with RC compare. .burst = FALSE, // Burst signal selection. .clki = FALSE, // Clock inversion. .tcclks = TC_CLOCK_SOURCE_TC2 // Internal source clock 2, connected to fPBA/2=15.5MHz. }; //! Timer/counter interrupts. static const tc_interrupt_t TC_INTERRUPT = { .etrgs = 0, //! External trigger interrupt. .ldrbs = 0, //! RB load interrupt. .ldras = 0, //! RA load interrupt. .cpcs = 1, //! RC compare interrupt. - generate interrupt with counter reaching Reset Count value (RC) .cpbs = 0, //! RB compare interrupt. .cpas = 0, //! RA compare interrupt. .lovrs = 0, //! Load overrun interrupt. .covfs = 0 //! Counter overflow interrupt. }; volatile avr32_tc_t *tc = EXAMPLE_TC; Disable_global_interrupt(); // Register the RTC interrupt handler to the interrupt controller. INTC_register_interrupt(&tc_irq, EXAMPLE_TC_IRQ, AVR32_INTC_INT0); Enable_global_interrupt(); // Initialize the timer/counter. tc_init_waveform(tc, &WAVEFORM_OPT); // Initialize the timer/counter waveform. // Set the compare triggers for timer/counter (TC). // TC counter is 16-bits, with secondary main clock TIMER_CLOCK2 = FPBA clock/2 = 33 MHz/2=15.5MHz. // Lowest possible freq is 15.5MHz/(2^16)=236Hz. // We want ADC sample rate of FADC Hz. To get this, we load RC (Reset Counter) value so that // TC reaches RC value every 1/FADC s. Therefore we configure TC so that RC=FPBA/FADC. // E.g., to get FADC=10kHz, we need RC=15.5MHz/10000=1550. // The timer interrupt will then run at 10kHz (verfied on scope). // The timer interrupt for debug will toggle PA10 which will result in a square wave at 5kHz (verified). tc_write_rc(tc, TC_CHANNEL, (FPBA /2) / FADC); // Set RC value. tc_configure_interrupts(tc, TC_CHANNEL, &TC_INTERRUPT); // Start the timer/counter. tc_start(tc, TC_CHANNEL); } ////////////////////////////////////////////////////////////////////////////// // main ////////////////////////////////////////////////////////////////////////////// int main() { Enable_global_exception(); Disable_global_interrupt(); INTC_init_interrupts(); pcl_switch_to_osc(PCL_OSC0, FOSC0, OSC0_STARTUP); init_clock(); init_pwm(); gpio_local_init(); init_adc(); pcl_configure_usb_clock(); usb_task_init(); init_tc(); #if USB_DEVICE_FEATURE == ENABLED device_task_init(); #endif gpio_local_enable_pin_output_driver(AVR32_PIN_PA10); // we bit bang these for debugging on scope gpio_local_enable_pin_output_driver(AVR32_PIN_PA11); gpio_local_enable_pin_output_driver(AVR32_PIN_PA12); gpio_local_clr_gpio_pin(AVR32_PIN_PA10); gpio_local_clr_gpio_pin(AVR32_PIN_PA11); gpio_local_clr_gpio_pin(AVR32_PIN_PA12); while (TRUE) { usb_task(); #if USB_DEVICE_FEATURE == ENABLED device_task(); #endif } return 0; } ////////////////////////////////////////////////////////////////////////////// // EOF //////////////////////////////////////////////////////////////////////////////
mic-servo.py
- mic-servo.py
#!/usr/bin/env python import sys, os import array import usb import colorsys import time import math busses = usb.busses() VENDOR = 0x03eb PRODUCT = 0x2300 IFACE = 0 EP_IN = 0x81 EP_OUT = 0x02 deltah = 0.0003 T = 0.00 PWMperADC = 100 BAR = 80 tstart = time.time() def get_device(): for bus in busses: devices = bus.devices for dev in devices: if dev.idVendor == VENDOR and dev.idProduct == PRODUCT: return dev return None vmax = 1 vmin = 2**16 vavg=0.0 vms=0.0 m=.02 # mixing factor for filter vavg=0.0 initialized=False vol=0 def adc(v): global vmax, vmin, m, vavg, vms, initialized, vol vol=v if vol > vmax: vmax = vol if vol < vmin: vmin = vol if vmax <= vmin: vmin -= 1 t = time.time() - tstart hz = usbiocnt / t # vol=int(255*(vol - vmin) / (vmax - vmin) ) o = '% 4d Hz ' % hz o+= '% 4d ' % vol o += '#' * int( BAR * (vol - vmin) / (vmax - vmin) ) print o usbiocnt = 0L def usbio(dh): global usbiocnt, vol usbiocnt += 1 dout = array.array('B', [0]*4) dout[0] = 0xFF & 0x00 dout[1] = 0xFF & (vol) # red dout[2] = 0xFF & (vol) # green dout[3] = 0xFF & (vol) # blue dh.bulkWrite(EP_OUT, dout.tostring()) #if usbiocnt % PWMperADC == 0: if 1: din = dh.bulkRead(EP_IN, 4) # read a packet l = len(din) if l != 4: print "unexpected bulk read length: %d" % l else: if usbiocnt % PWMperADC == 0: value=(din[0]<<24)+ (din[1]<<16)+ (din[2] << 8) + din[3] adc(value) def intcol(v): v = int(v*256) if v > 255: v = 255 if v < 0: v = 0 return v def micservo(dh): global vol while 1: usbio(dh) time.sleep(T) def main(): dev = get_device() dh = dev.open() dh.claimInterface(IFACE) micservo(dh) dh.releaseInterface() del dh return 0 if __name__ == '__main__': sys.exit( main() )
dig/mic-servo.txt · Last modified: 2024/02/29 07:28 by 127.0.0.1