I added code to the receiving function to conditionally run the servo based on the received value. I’m getting strange behavior from the Wixel when I include a while statement nested within an if statement. After downloading the program, the Wixel was suddenly not recognized by the computer and the LEDs weren’t on. I could only restore the USB connection and download a different program to it by manually putting the Wixel into bootloader mode. The while statements are commented out in the radioToUsbService() function. Do you know why this logic could cause this problem?
#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <stdio.h>
#include <radio_queue.h>
/* VARIABLES ******************************************************************/
uint8 XDATA response[32];
PDATA volatile uint32 pwmCounter = 0; /* ticks of the pwm timer */
PDATA uint8 pwmPeriod = 200; /* how long the pwm wave lasts for: 20ms @ 10KHz */
PDATA volatile uint32 pwmPeriodNext = 200; /* when the next pwm period starts*/
PDATA volatile uint8 pwmPulse = 0; /* how long the pwm pulse last for: 1.5ms @ 10KHz */
PDATA volatile uint32 pwmPulseEnd = 15; /* when the pwm pulse ends */
int32 CODE param_input_mode = 0;
int32 CODE param_report_period_ms = 20;
/* FUNCTIONS ******************************************************************/
ISR(T3, 2) {
pwmCounter++;
if (pwmCounter == pwmPeriodNext)
{
if (pwmPulse)
{
setDigitalOutput(1, HIGH);
}
else
{
setDigitalOutput(1, LOW);
}
pwmPulseEnd = pwmCounter + pwmPulse;
pwmPeriodNext += pwmPeriod;
}
else if (pwmCounter == pwmPulseEnd)
{
setDigitalOutput(1, LOW);
}
}
void analogInputsInit()
{
switch(param_input_mode)
{
case 1: // Enable pull-up resistors for all pins on Port 0.
//This shouldn't be necessary because the pull-ups are enabled by default.
P2INP &= ~(1<<5); //PDUP0 = 0: Pull-ups on Port 0.
P0INP = 0;
break;
case -1: //Enable pull-down resistors for all pins on Port 0.
P2INP |= (1<<5);
P0INP = 0;
break;
default: //Disable pull-ups and pull-downs for all pins on Port 0.
P0INP = 0x3F;
break;
}
}
void adcToRadioService()
{
static uint16 lastTx = 0;
uint8 XDATA * txPacket;
//Check to see if it's time to send a report and if there's a radio TX buffer available.
if ((uint16)(getMs()-lastTx) >= param_report_period_ms && (txPacket = radioQueueTxCurrentPacket()))
{
//Both of those conditions are true, so send a report.
uint8 i;
uint16 XDATA * ptr = (uint16 XDATA *)&txPacket[5];
//This should be done before all the ADC readings, which take about 3 ms.
lastTx = getMs();
//Byte 0 is the length.
txPacket[0] = 16;
//Bytes 1-4 are the serial number.
txPacket[1] = serialNumber[0];
txPacket[2] = serialNumber[1];
txPacket[3] = serialNumber[2];
txPacket[4] = serialNumber[3];
adcSetMillivoltCalibration(adcReadVddMillivolts());
//Bytes 5-16 are the ADC readings on channels 0-6.
for (i = 0; i < 6; i++)
{
*(ptr++) = adcConvertToMillivolts(adcRead(i));
}
radioQueueTxSendPacket();
}
}
void updateLeds() {
usbShowStatusWithGreenLed();
}
void softPwmInit() {
T3CC0 = 75;
T3IE = 1; // Enable Timer 3 interrupt.
// DIV=101: 1:32 prescaler
// START=1: Start the timer
// OVFIM=1: Enable the overflow interrupt.
// MODE=10: Modulo
T3CTL = 0b10111010;
EA = 1; // Globally enable interrupts.
}
typedef struct adcReport
{
uint8 length;
uint8 serialNumber[4];
uint16 readings[6];
} adcReport;
void putchar(char c)
{
usbComTxSendByte(c);
}
void radioToUsbService()
{
adcReport XDATA * rxPacket;
//Check if there is a radio packet to report and space in the USB TX buffers to report it.
if ((rxPacket = (adcReport XDATA *)radioQueueRxCurrentPacket()) && usbComTxAvailable() >= 64)
{
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[0]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
if((rxPacket->readings[5]) < 1250){
//while((rxPacket->readings[5]) < 2015){
pwmPulse = 16;
if ((rxPacket = (adcReport XDATA *)radioQueueRxCurrentPacket()) && usbComTxAvailable() >= 64)
{
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[0]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
//}
pwmPulse = 0;
}
else if((rxPacket->readings[5]) > 2015){
//while((rxPacket->readings[5]) > 1250){
pwmPulse = 16;
if ((rxPacket = (adcReport XDATA *)radioQueueRxCurrentPacket()) && usbComTxAvailable() >= 64)
{
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[0]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
//}
pwmPulse = 0;
}
else{
pwmPulse = 0;
return 0;
}
}
void main() {
systemInit();
usbInit();
softPwmInit();
analogInputsInit();
radioQueueInit();
while(1)
{
boardService();
updateLeds();
usbComService();
radioToUsbService(); //receive accelerometer values
adcToRadioService(); //transmit accelerometer values
}
}
you have to keep calling the “stayalive” routines inside tight loops else the radio doesn’t get serviced!
so inside your “while” loop include a call to
Thank you for the suggestion…unfortunately I still get the same problem! This function call is inside the radioToUsbService() function, right? I’m confused about why the radioToUsbService() function is being called within the stayAlive function.
Thanks, that solved the hardware problem!
Now my problem is that the logic in my program isn’t working because after I download the same program to both Wixels, their servos run continuously. Only one Wixel’s servo should be running as it’s receiving accelerometer values. I tried to debug by viewing the terminal window, but nothing is printed. Any suggestions? Thanks!
The indentation of your program makes it very hard to read. Eclipse has a “Correct Indentation” command in the Source menu you can use. Also I recommend configuring Eclipse to not use tab characters and use spaces instead.
In radioToUsbService, you are reading values from rxPacket even though rxPacket might be a null pointer (0). This will give you unexpected results because you will be reading from the wrong part of memory. When rxPacket is 0, it means that there is no packet to be processed.
#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <stdio.h>
#include <radio_queue.h>
/* VARIABLES ******************************************************************/
uint8 XDATA response[32];
PDATA volatile uint32 pwmCounter = 0; /* ticks of the pwm timer */
PDATA uint8 pwmPeriod = 200; /* how long the pwm wave lasts for: 20ms @ 10KHz */
PDATA volatile uint32 pwmPeriodNext = 200; /* when the next pwm period starts*/
PDATA volatile uint8 pwmPulse = 0; /* how long the pwm pulse last for: 1.5ms @ 10KHz */
PDATA volatile uint32 pwmPulseEnd = 15; /* when the pwm pulse ends */
int32 CODE param_input_mode = 0;
int32 CODE param_report_period_ms = 20;
/* FUNCTIONS ******************************************************************/ISR(T3, 2) {
pwmCounter++;
if (pwmCounter == pwmPeriodNext) {
if (pwmPulse) {
setDigitalOutput(1, HIGH);
} else {
setDigitalOutput(1, LOW);
}
pwmPulseEnd = pwmCounter + pwmPulse;
pwmPeriodNext += pwmPeriod;
} else if (pwmCounter == pwmPulseEnd) {
setDigitalOutput(1, LOW);
}
}
void analogInputsInit() {
switch (param_input_mode) {
case 1: // Enable pull-up resistors for all pins on Port 0.
//This shouldn't be necessary because the pull-ups are enabled by default.
P2INP &= ~(1 << 5); //PDUP0 = 0: Pull-ups on Port 0.
P0INP = 0;
break;
case -1: //Enable pull-down resistors for all pins on Port 0.
P2INP |= (1 << 5);
P0INP = 0;
break;
default: //Disable pull-ups and pull-downs for all pins on Port 0.
P0INP = 0x3F;
break;
}
}
void adcToRadioService() {
static uint16 lastTx = 0;
uint8 XDATA * txPacket;
//Check to see if it's time to send a report and if there's a radio TX buffer available.
if ((uint16) (getMs() - lastTx) >= param_report_period_ms && (txPacket
= radioQueueTxCurrentPacket())) {
//Both of those conditions are true, so send a report.
uint8 i;
uint16 XDATA * ptr = (uint16 XDATA *) &txPacket[5];
//This should be done before all the ADC readings, which take about 3 ms.
lastTx = getMs();
//Byte 0 is the length.
txPacket[0] = 16;
//Bytes 1-4 are the serial number.
txPacket[1] = serialNumber[0];
txPacket[2] = serialNumber[1];
txPacket[3] = serialNumber[2];
txPacket[4] = serialNumber[3];
adcSetMillivoltCalibration(adcReadVddMillivolts());
//Bytes 5-16 are the ADC readings on channels 0-6.
for (i = 0; i < 6; i++) {
*(ptr++) = adcConvertToMillivolts(adcRead(i));
}
radioQueueTxSendPacket();
}
}
void stayAlive() {
boardService();
updateLeds();
usbComService();
}
void updateLeds() {
usbShowStatusWithGreenLed();
}
void softPwmInit() {
T3CC0 = 75;
T3IE = 1; // Enable Timer 3 interrupt.
// DIV=101: 1:32 prescaler
// START=1: Start the timer
// OVFIM=1: Enable the overflow interrupt.
// MODE=10: Modulo
T3CTL = 0b10111010;
EA = 1; // Globally enable interrupts.
}
typedef struct adcReport {
uint8 length;
uint8 serialNumber[4];
uint16 readings[6];
} adcReport;
void putchar(char c) {
usbComTxSendByte(c);
}
void radioToUsbService() {
adcReport XDATA * rxPacket;
//Check if there is a radio packet to report and space in the USB TX buffers to report it.
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[0]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
if ((rxPacket->readings[5]) < 1250) {
while ((rxPacket->readings[5]) < 2015) {
stayAlive();
pwmPulse = 16;
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[0]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
}
pwmPulse = 0;
}
else if ((rxPacket->readings[5]) > 2015) {
while ((rxPacket->readings[5]) > 1250) {
stayAlive();
pwmPulse = 16;
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[0]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
}
pwmPulse = 0;
}
else {
pwmPulse = 0;
}
}
void main() {
systemInit();
usbInit();
softPwmInit();
analogInputsInit();
radioQueueInit();
while (1) {
boardService();
updateLeds();
usbComService();
radioToUsbService(); //receive accelerometer values
adcToRadioService(); //transmit accelerometer values
}
}
In radioToUsbService, I thought I was following the same format of code in the wireless_adc_rx app. Doesn’t the if ((rxPacket = (adcReport XDATA *)radioQueueRxCurrentPacket()) && usbComTxAvailable() >= 64) statement check to see if there’s a packet to report?
Yes, it does, but the code you wrote to read rxPacket is outside the if statement so it will run regardless of whether rxPacket is null or not. That’s just how if statements work:
void foo()
{
if (x)
{
... // This code runs if x is true (non-zero)
}
... // This code always runs assuming there is nothing special above to alter the flow (like a return statement)
}
Thanks, I didn’t realize it was outside of the if statement. I’m still having trouble with my program because the back and forth communication and running the servo conditionally in the receiving function isn’t working. I was wondering if this method makes any sense:
#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <stdio.h>
#include <radio_queue.h>
/* VARIABLES ******************************************************************/
uint8 XDATA response[32];
PDATA volatile uint32 pwmCounter = 0; /* ticks of the pwm timer */
PDATA uint8 pwmPeriod = 200; /* how long the pwm wave lasts for: 20ms @ 10KHz */
PDATA volatile uint32 pwmPeriodNext = 200; /* when the next pwm period starts*/
PDATA volatile uint8 pwmPulse = 0; /* how long the pwm pulse last for: 1.5ms @ 10KHz */
PDATA volatile uint32 pwmPulseEnd = 15; /* when the pwm pulse ends */
int32 CODE param_input_mode = 0;
int32 CODE param_report_period_ms = 20;
/* FUNCTIONS ******************************************************************/ISR(T3, 2) {
pwmCounter++;
if (pwmCounter == pwmPeriodNext) {
if (pwmPulse) {
setDigitalOutput(1, HIGH);
} else {
setDigitalOutput(1, LOW);
}
pwmPulseEnd = pwmCounter + pwmPulse;
pwmPeriodNext += pwmPeriod;
} else if (pwmCounter == pwmPulseEnd) {
setDigitalOutput(1, LOW);
}
}
void analogInputsInit() {
switch (param_input_mode) {
case 1: // Enable pull-up resistors for all pins on Port 0.
//This shouldn't be necessary because the pull-ups are enabled by default.
P2INP &= ~(1 << 5); //PDUP0 = 0: Pull-ups on Port 0.
P0INP = 0;
break;
case -1: //Enable pull-down resistors for all pins on Port 0.
P2INP |= (1 << 5);
P0INP = 0;
break;
default: //Disable pull-ups and pull-downs for all pins on Port 0.
P0INP = 0x3F;
break;
}
}
void adcToRadioService() {
static uint16 lastTx = 0;
uint8 XDATA * txPacket;
//Check to see if it's time to send a report and if there's a radio TX buffer available.
if ((uint16) (getMs() - lastTx) >= param_report_period_ms && (txPacket
= radioQueueTxCurrentPacket())) {
//Both of those conditions are true, so send a report.
uint8 i;
uint16 XDATA * ptr = (uint16 XDATA *) &txPacket[5];
//This should be done before all the ADC readings, which take about 3 ms.
lastTx = getMs();
//Byte 0 is the length.
txPacket[0] = 16;
//Bytes 1-4 are the serial number.
txPacket[1] = serialNumber[0];
txPacket[2] = serialNumber[1];
txPacket[3] = serialNumber[2];
txPacket[4] = serialNumber[3];
adcSetMillivoltCalibration(adcReadVddMillivolts());
//Bytes 5-16 are the ADC readings on channels 0-6.
for (i = 0; i < 6; i++) {
*(ptr++) = adcConvertToMillivolts(adcRead(i));
}
radioQueueTxSendPacket();
}
}
void stayAlive() {
boardService();
updateLeds();
usbComService();
}
void updateLeds() {
usbShowStatusWithGreenLed();
}
void softPwmInit() {
T3CC0 = 75;
T3IE = 1; // Enable Timer 3 interrupt.
// DIV=101: 1:32 prescaler
// START=1: Start the timer
// OVFIM=1: Enable the overflow interrupt.
// MODE=10: Modulo
T3CTL = 0b10111010;
EA = 1; // Globally enable interrupts.
}
typedef struct adcReport {
uint8 length;
uint8 serialNumber[4];
uint16 readings[6];
} adcReport;
void putchar(char c) {
usbComTxSendByte(c);
}
void radioToUsbService() {
adcReport XDATA * rxPacket;
//Check if there is a radio packet to report and space in the USB TX buffers to report it.
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[5]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
if ((rxPacket->readings[5]) < 1250) {
while ((rxPacket->readings[5]) < 2015) {
stayAlive();
pwmPulse = 16;
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[5]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
}
pwmPulse = 0;
}
else if ((rxPacket->readings[5]) > 2015) {
while ((rxPacket->readings[5]) > 1250) {
stayAlive();
pwmPulse = 16;
if ((rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket())
&& usbComTxAvailable() >= 64) {
//We received a packet from a Wixel
printf(" %5u", rxPacket->readings[5]);
putchar('\r');
putchar('\n');
radioQueueRxDoneWithPacket();
}
}
pwmPulse = 0;
}
else {
pwmPulse = 0;
}
}
}
void main() {
systemInit();
usbInit();
softPwmInit();
analogInputsInit();
radioQueueInit();
while (1) {
boardService();
updateLeds();
usbComService();
radioToUsbService(); //receive accelerometer values
adcToRadioService(); //transmit accelerometer values
}
}
I would not expect that code to work, because once again you are reading from rxPacket without first guaranteeing that it is non-zero. You are doing that in the conditions of your while loops.
I still recommend that you try to do something simpler where both Wixels are periodically transmitting no matter what state the accelerometers are in. The code would look something like this:
void radioRxService()
{
adcReport XDATA * rxPacket;
if (rxPacket = (adcReport XDATA *) radioQueueRxCurrentPacket()) {
... // Copy the accelerometer data from the rxPacket to other variables so you can easily access it later.
radioQueueRxDoneWithPacket();
}
}
void servoService()
{
... // Using the accelerometer data from this Wixel and the stored data from the other Wixel, set the servo positions.
}
If you want to do hysteresis, you can add some extra variables instead of creating while loops.
I’d like to elaborate a little on why I think this is a good approach. Basically what I am doing is breaking down your program into small, simple subtasks that have well-defined inputs and outputs.
radioTxService (a.k.a. adcToRadioService)
Input: Information (i.e. accelerometer readings) from this Wixel.
Output: Packets to be sent to the radioQueue library.
radioRxService
Input: Packets from the radioQueue library.
Output: Information about the other Wixel, stored in RAM.
servoService
Input: Information about the other Wixel, stored in RAM. Information about this Wixel.
Output: Servo position.
All of these functions would be non-blocking (no long-running while loops).
Breaking the program up into small, simple tasks like this has several advantages. First of all, the functions you create will be more general, so you will be able to use them in the future with minor modifications. For example, radioTxService and radioRxService are general functions for synchronizing a data structure between two Wixels, and they can easily be adapted to data types other than accelerometer readings. For another example, the servoService function doesn’t need to know or care that a radio was used to receive the data from the other Wixel; it will work no matter what type of transport was used as long as the data has been stored in the right place. Secondly, when you break things up like this, each individual function will have a smaller amount of work to do and a smaller number of inputs and outputs, which means it will be easier to understand, write, maintain, and debug. Instead of just telling me that your overall system “isn’t working” and then presenting me with some complex code to debug, you’ll be able to print debugging information to the USB COM port and figure out which of the tasks is not doing its job.
I got it to work but I have one more problem! I noticed that one Wixel’s accelerometer ADC reading has a different range (with a maximum of about 1945 mV) than the other Wixel’s accelerometer reading (maximum of 2030 mV). I thought using the 1.25 V internal reference might make the readings more accurate, but after using adcConvertToMillivolts(adcRead(5| ADC_REFERENCE_INTERNAL)), the terminal window displays values of around 3330 mV, which don’t change when the accelerometer rotates. My IR distance sensor doesn’t have this problem when I use the internal reference in adcRead.
If you use the internal 1.25 V reference, then you can only measure voltages between 0 and 1.25 V. This is explained in the documentation of adcRead: pololu.github.com/wixel-sdk/adc_ … c3a0cf1789
The argument to adcConvertToMillivolts is supposed to be “an ADC result between -2048 and 2047 that was measured using VDD as a reference”. This is explained in the documentation of adcConvertToMillivolts: pololu.github.com/wixel-sdk/adc_ … 2892254a33
If you want to make the readings more accurate, you should try calling running this once in a while: adcSetMillivoltCalibration(adcReadVddMillivolts());
Thanks! I’m actually calling adcSetMillivoltCalibration(adcReadVddMillivolts()); regularly already. Do you know why I could be getting different ranges for each Wixel/accelerometer or do you think it’s an insignificant difference?
It could be significant depending on what you want to do with the numbers. Does you application need a lot of precision?
When a system with two components is not behaving as I expect it to, I like to look at the signals between the two components to figure out which of the two components is not behaving as expected. You could measure the voltage on the accelerometer outputs and that would give us a good clue about what’s going on.
The voltage I measured at the accelerometer outputs was about 100 mV more than the voltage being printed in the terminal window. One accelerometer had a voltmeter reading of 1643 mV and on the computer it was 1580 mV. The other accelerometer had a voltmeter reading of 1560 mV and on the computer it was around 1460 mV.
For my application, the more precision the better because an action is performed when the accelerometer reading is beyond a threshold value.
The fact that you are using a threshold does not imply that you need a lot of precision. You also need to think about what kind of signal the threshold is being applied to. If the signal changes very slowly (i.e. you’re measuing the angle of the hour hand of a clock) then a small inaccuracy in the threshold could correspond to a difference of several minutes in the time it takes to trigger the threshold, which could conceivably be bad for your application. On the other hand, if you are measuring a signal that quickly spikes way above the threshold once in a while (i.e. the acceleration of a human hand making gestures), then the precise value of the threshold doesn’t matter that much.
I’m not sure what could be causing the 100 mV discrepancy you are seeing. In case there is some residual charge left in the ADC from another channel, I would try taking 2-6 ADC readings on the channel you want to read, and throwing out every result except the last one. I would also consult the CC2511 datasheet and learn about what I can do to make the ADC more accurate.
It just occurred to me that there’s no reason I need to convert the raw ADC values to millivolts for my application. I wanted to test how accurate the ADC values were using the internal 1.25 V reference, but after adding the ADC_REFERNCE_INTERNAL parameter to adcRead, the values hardly changed from “2047” while rotating the accelerometer.
here’s the code:
/*
* This program outputs the raw ADC accelerometer values on P0_5.
*
*/
#include <wixel.h>
#include <time.h>
#include <usb.h>
#include <usb_com.h>
#include <adc.h>
#include <stdio.h>
uint32 lastToggle = 0;
void analogInputsInit() {
//Disable pull-ups and pull-downs for all pins on Port 0.
P0INP = 0x3F;
}
void putchar(char c)
{
usbComTxSendByte(c);
}
void updateprint()
{
if (getMs() - lastToggle >= 1000)
{
if (usbComTxAvailable() >= 64)
{
printf("adc: %d\r\n", adcRead(5 | ADC_REFERENCE_INTERNAL));
}
lastToggle = getMs();
}
}
void main()
{
systemInit();
usbInit();
analogInputsInit();
while(1)
{
boardService();
updateprint();
usbComService();
usbShowStatusWithGreenLed();
LED_RED(1);
}
}
Is there something about the ADC_REFERENCE_INTERNAL parameter I’m missing?
Yes. If you use the internal 1.25 V reference, then you can only measure voltages between 0 and 1.25 V. This is explained in the documentation of adcRead: pololu.github.com/wixel-sdk/adc_ … c3a0cf1789