a project with pwm and adc

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderators: RoccoMarco, barthess

XXzzz
Posts: 15
Joined: Wed Dec 20, 2017 8:14 am
Has thanked: 3 times
Been thanked: 1 time

a project with pwm and adc

Postby XXzzz » Fri Dec 29, 2017 10:25 am

I use the pwm to control the motor and adc to get Sampling current.
1.in pwm chanel callback

Code: Select all

static void pwmc1cb(PWMDriver *pwmp)
{
  (void)pwmp;

  chSysLockFromISR();

  adcStartConversionI(&ADCD1, &adcgrpcfg, samples, ADC_GRP_BUF_DEPTH);

  chSysUnlockFromISR();

}


2. in adc callback

Code: Select all

static void adccallback(ADCDriver *adcp, adcsample_t *buffer, size_t n)
{
  (void)adcp;
  (void)buffer;
  (void)n;

  chSysLockFromISR();
  adcStopConversionI(&ADCD1);
  chBSemSignalI(&current_bsem);
  chSysUnlockFromISR();

}

3. create a thread to get the samples after signall

Code: Select all

static THD_WORKING_AREA(waThread2, 128);
static THD_FUNCTION(Thread2, arg)
{
  (void)arg;
  chRegSetThreadName("current");
  while (true)
  {
    msg_t msg = chBSemWait(&current_bsem);
    (void)msg;
    float ret = getCurrent(motor1.state);
    (void)ret;
  }
}

(getCurrent can get adc samples and transform it to current)
now the thread doesn't work ,mabe i need use other way to get the current on PWM_ACTIVE voltage?

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: a project with pwm and adc

Postby Giovanni » Fri Dec 29, 2017 10:35 am

Is the semaphore initialized to "taken"?

You should consider HW triggering for the ADC, it would perform a continuous circular conversion triggered by the PWM timer. You need to verify if your timer is one of the possible trigger sources for ADC.

Also, is it worth using a thread for this? simple processing could be done directly in the ADC callback.

Giovanni

XXzzz
Posts: 15
Joined: Wed Dec 20, 2017 8:14 am
Has thanked: 3 times
Been thanked: 1 time

Re: a project with pwm and adc

Postby XXzzz » Fri Dec 29, 2017 11:10 am

now i change my code to test my callback ,clear the light in callback ,and set the light in thread ,and callback is working,

Code: Select all

static void pwmc1cb(PWMDriver *pwmp)
{
  (void)pwmp;

  chSysLockFromISR();
  adcStartConversionI(&ADCD1, &adcgrpcfg, samples, ADC_GRP_BUF_DEPTH);
  chSysUnlockFromISR();
  palClearPad(GPIOG, GPIOG_LED_2);
}

static void adccallback(ADCDriver *adcp, adcsample_t *buffer, size_t n)
{
  (void)adcp;
  (void)buffer;
  (void)n;

  chSysLockFromISR();
  adcStopConversionI(&ADCD1);
  chBSemSignalI(&current_bsem);
  chSysUnlockFromISR();
  palClearPad(GPIOG, GPIOG_LED_3);
}

static THD_WORKING_AREA(waThread2, 128);
static THD_FUNCTION(Thread2, arg)
{
  (void)arg;
  chRegSetThreadName("current");
  while (true)
  {
    msg_t msg = chBSemWait(&current_bsem);
    (void)msg;
    float ret = getCurrent(motor1.state);
    (void)ret;
  }
}

static THD_WORKING_AREA(waThread1, 128);
static THD_FUNCTION(Thread1, arg)
{
  (void)arg;
  chRegSetThreadName("blinker");
  while (true)
  {
    palSetPad(GPIOG, GPIOG_LED_2);
    palSetPad(GPIOG, GPIOG_LED_3);
   
    palSetPad(GPIOB, GPIOB_LED_1);
    chThdSleepMilliseconds(500);
    palClearPad(GPIOB, GPIOB_LED_1);
    chThdSleepMilliseconds(500);
  }
}


i also have some info to print in the getCurrent()
and chBSemObjectInit(&current_bsem, TRUE); been call after the pwm init
so it willbe like this pwm init->sem init->adc init ->pwm output->pwm callback->adc start Conversion ->adc callback->signal and adc stop Conversion ->getCurrent in the thread. and pwm call back will run again
so i can get the current in the active voltage,
and now the thread is running ,and the pwm callback ,adc callback also working ,but the thread seem can't recv the signal

XXzzz
Posts: 15
Joined: Wed Dec 20, 2017 8:14 am
Has thanked: 3 times
Been thanked: 1 time

Re: a project with pwm and adc

Postby XXzzz » Fri Dec 29, 2017 12:17 pm

now i do a new test on stm32f103zet6

Code: Select all


#include <stdio.h>
#include <string.h>

#include "ch.h"
#include "hal.h"
#include "ch_test.h"
#include "usbcfg.h"
#include "myshell.h"

#define SHELL_WA_SIZE THD_WORKING_AREA_SIZE(2048)
binary_semaphore_t bsem;

static void pwmpcb(PWMDriver *pwmp)
{
  (void)pwmp;
  palSetPad(GPIOB, GPIOB_LED_1);
  chSysLockFromISR();
  chBSemSignalI(&bsem);
  chSysUnlockFromISR();
}

static void pwmc1cb(PWMDriver *pwmp)
{

  (void)pwmp;
  palClearPad(GPIOB, GPIOB_LED_1);
  chSysLockFromISR();
  chBSemSignalI(&bsem);
  chSysUnlockFromISR();
}

static PWMConfig pwmcfg = {
    1000,
    10000, //pwm 10s
    pwmpcb,
    {{PWM_OUTPUT_ACTIVE_HIGH, pwmc1cb},
     {PWM_OUTPUT_DISABLED, NULL},
     {PWM_OUTPUT_DISABLED, NULL},
     {PWM_OUTPUT_DISABLED, NULL}},
    0,
    0,
#if STM32_PWM_USE_ADVANCED
    0
#endif
};

/*
 * blinker
 */
static THD_WORKING_AREA(waThread1, 128);
static THD_FUNCTION(Thread1, arg)
{
  (void)arg;
  chRegSetThreadName("blinker");
  while (true)
  {
    msg_t msg = chBSemWait(&bsem);
    palClearPad(GPIOG, GPIOG_LED_2);
    (void)msg;
  }
}

int main(void)
{

  halInit();
  chSysInit();

  //Bsem  init
  chBSemObjectInit(&bsem, TRUE);
  /*
   * pwm init
   */
  pwmStart(&PWMD1, &pwmcfg);
  pwmEnablePeriodicNotification(&PWMD1);
  palSetPadMode(IOPORT1, 8, PAL_MODE_STM32_ALTERNATE_PUSHPULL); /*PA8*/

  /*
   * 75%
   */
  pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 7500));
  pwmEnableChannelNotification(&PWMD1, 0);
  chThdSleepMilliseconds(500000);

  /*
   * stop pwm
   */
  pwmDisableChannel(&PWMD1, 0);
  pwmStop(&PWMD1);

  /*
   * creat blinker thread
   */
  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);

  while (true)
  {
  }
}


and the led1 has been set and clear in the callback but the led2 not been clear,
i think the thread dosn't recv the signal.
and the code about sem are right?

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: a project with pwm and adc

Postby Giovanni » Fri Dec 29, 2017 2:25 pm

Hi,

That empty while in your main(), it does not release CPU for other threads at equal or lower priority. Put a chThdSleep() in there.

Giovanni

XXzzz
Posts: 15
Joined: Wed Dec 20, 2017 8:14 am
Has thanked: 3 times
Been thanked: 1 time

Re: a project with pwm and adc

Postby XXzzz » Sun Dec 31, 2017 10:23 am

now i change the thread to this

Code: Select all

static THD_WORKING_AREA(waThread2, 128);
static THD_FUNCTION(Thread2, arg)
{
  (void)arg;
  chRegSetThreadName("current");
  while (true)
  {
    msg_t msg = chBSemWaitTimeout(&current_bsem, MS2ST(500));
    /* time out*/
    if (msg == MSG_TIMEOUT)
    {
      chprintf((BaseSequentialStream *)&SD2, "\r\ncurrent thread timeout\r\n");
      continue;
    }
    float ret = getCurrent(motor1.state);
    chThdSleepMilliseconds(500);
  }
}

and it's working , maybe the reason is when pwminit->adcinit->creatthread
this thread is blocked and wait for a Bsem and it's blocked too much time .
but i can't understand :why it can't work ? i think it could wait and get the sem
and this thraed use chBSemWaitTimeout() seems just not blocked.
i want know why ,maybe i need learn more about the Priorities and Scheduling or how does the scheduler work?

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: a project with pwm and adc

Postby Giovanni » Sun Dec 31, 2017 10:57 am

The rule is very simple and is valid for most RTOSes:

Among the threads not sleeping/waiting, the one with highest priority is the being executed.

A thread not releasing CPU is going to block all other threads to equal or lower priority.

Giovanni

XXzzz
Posts: 15
Joined: Wed Dec 20, 2017 8:14 am
Has thanked: 3 times
Been thanked: 1 time

Re: a project with pwm and adc

Postby XXzzz » Sun Dec 31, 2017 2:24 pm

now my code has another bug..,
when i set the depth=8,and the callback return the n =4,
depth=16,n=8;
this is my test code ,it work like pwmstart->pwmcallback->adcStartConversion->adccallback->Bsem->thread
then the thread print the samples buffer and n.

Code: Select all


#include <stdio.h>
#include <string.h>

#include "ch.h"
#include "hal.h"
#include "ch_test.h"

#include "myshell.h"

#define CHP ((BaseSequentialStream *)&SD2)
#define SHELL_WA_SIZE THD_WORKING_AREA_SIZE(2048)
#define ADC_GRP_NUM_CHANNELS 3 //adc 通道数
#define ADC_GRP_BUF_DEPTH 16   //数据深度

int samplessize = 0;
binary_semaphore_t bsem, errsem;
adcsample_t samples[ADC_GRP_NUM_CHANNELS * ADC_GRP_BUF_DEPTH]; //buffer

/**
 * @brief       ADC
 *
 */
static void adccallback(ADCDriver *adcp, adcsample_t *buffer, size_t n)
{
  (void)adcp;
  (void)buffer;
  (void)n;
  //锁定进入isr
  chSysLockFromISR();
  samplessize = n;
  chBSemSignalI(&bsem); //发送信号

  adcStopConversionI(&ADCD1); //停止adc转换
  //解锁
  chSysUnlockFromISR();
}

static void adcerrorcallback(ADCDriver *adcp, adcerror_t err)
{
  (void)adcp;
  (void)err;
  chSysLockFromISR();

  // chBSemSignalI(&errsem); //发送信号

  chSysUnlockFromISR();
  palClearPad(GPIOB, GPIOB_LED_1);
}

const ADCConversionGroup adcgrpcfg = {
    TRUE,
    ADC_GRP_NUM_CHANNELS,
    adccallback,
    adcerrorcallback,
    0,
    ADC_CR2_TSVREFE, /*  CR2 */
    ADC_SMPR1_SMP_VREF(ADC_SAMPLE_239P5) | ADC_SMPR1_SMP_AN11(ADC_SAMPLE_239P5) | ADC_SMPR1_SMP_AN10(ADC_SAMPLE_239P5),
    0, /* SMPR2 */
    ADC_SQR1_NUM_CH(ADC_GRP_NUM_CHANNELS),
    0,
    ADC_SQR3_SQ1_N(ADC_CHANNEL_VREFINT) | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN10) | ADC_SQR3_SQ3_N(ADC_CHANNEL_IN11)};

/**
 * @brief       PWM
 *
 */
static void pwmpcb(PWMDriver *pwmp)
{
  (void)pwmp;
}

static void pwmc1cb(PWMDriver *pwmp)
{

  (void)pwmp;

  chSysLockFromISR();
  // chBSemSignalI(&bsem);
  adcStartConversion(&ADCD1, &adcgrpcfg, samples, ADC_GRP_BUF_DEPTH);
  chSysUnlockFromISR();
}

static PWMConfig pwmcfg = {
    100000,
    1000, //pwm 10s
    pwmpcb,
    {{PWM_OUTPUT_ACTIVE_HIGH, pwmc1cb},
     {PWM_OUTPUT_DISABLED, NULL},
     {PWM_OUTPUT_DISABLED, NULL},
     {PWM_OUTPUT_DISABLED, NULL}},
    0,
    0,
#if STM32_PWM_USE_ADVANCED
    0
#endif
};

/*
 * blinker
 */
static THD_WORKING_AREA(waThread1, 128);
static THD_FUNCTION(Thread1, arg)
{
  (void)arg;
  chRegSetThreadName("blinker");
  while (true)
  {
    msg_t msg = chBSemWaitTimeout(&bsem, MS2ST(1000));
    /* time out*/
    if (msg == MSG_TIMEOUT)
    {
      chprintf((BaseSequentialStream *)&SD2, "\r\ncurrent thread timeout\r\n");
      continue;
    }
    chprintf((BaseSequentialStream *)&SD2, "samples size=%d\r\n", samplessize);
    for (int i = 0; i < ADC_GRP_NUM_CHANNELS; i++)
    {
      for (int j = 0; j < ADC_GRP_BUF_DEPTH; j++)
        chprintf((BaseSequentialStream *)&SD2, "%d\t", samples[i + j * ADC_GRP_NUM_CHANNELS]);
      chprintf((BaseSequentialStream *)&SD2, "\r\n");
    }
    chprintf((BaseSequentialStream *)&SD2, "\r\n\r\n");
  }
}

int main(void)
{

  halInit();
  chSysInit();
  shellInit();
  sdStart(&SD2, NULL);
  //Bsem  init
  chBSemObjectInit(&bsem, TRUE);
  // chBSemObjectInit(&errsem, TRUE);

  /*
   * pwm init
   */
  pwmStart(&PWMD1, &pwmcfg);
  pwmEnablePeriodicNotification(&PWMD1);
  palSetPadMode(IOPORT1, 8, PAL_MODE_STM32_ALTERNATE_PUSHPULL); /*PA8*/
  /*
   * adc init
   */
  //1.初始化GPIO
  palSetGroupMode(GPIOC, PAL_PORT_BIT(0) | PAL_PORT_BIT(1),
                  0, PAL_MODE_INPUT_ANALOG);
  //2.开启adc
  adcStart(&ADCD1, NULL);

  /*
   * 75%
   */
  pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 7500));
  pwmEnableChannelNotification(&PWMD1, 0);
  // chThdSleepMilliseconds(500000);

  /*
   * stop pwm
   */
  // pwmDisableChannel(&PWMD1, 0);
  // pwmStop(&PWMD1);

  /*
   * creat blinker thread
   */
  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);

  while (true)
  {

    thread_t *shelltp = chThdCreateFromHeap(NULL, SHELL_WA_SIZE,
                                            "shell", NORMALPRIO + 1,
                                            shellThread, (void *)&shell_cfg1);
    chThdWait(shelltp); /* Waiting termination.             */

    chThdSleepMilliseconds(1000);
  }
}

this is print data

Code: Select all

//when adc depth=8
samples size=4
1507   1507   1509   1508   0   0   0   0   
0   0   0   7   0   0   0   0   
0   0   5   8   0   0   0   0
//when adc depth=16
samples size=8
1508   1508   1509   1509   1509   1509   1509   1510   0   0   0   0   0   0   0   0   
3   0   6   53   0   0   0   0   0   0   0   0   0   0   0   0   
7   4   0   6   0   0   0   0   0   0   0   0   0   0   0   0   


i read the doc again ,in the Driver State Machine, maybe the callback will be called twice:once is half and another is full?

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: a project with pwm and adc

Postby Giovanni » Sun Dec 31, 2017 3:46 pm

The callback is called twice for circular conversions, you are doing a circular conversion there. One callback at half buffer another one at the end.

Giovanni

XXzzz
Posts: 15
Joined: Wed Dec 20, 2017 8:14 am
Has thanked: 3 times
Been thanked: 1 time

Re: a project with pwm and adc

Postby XXzzz » Mon Jan 01, 2018 9:00 am

i got that.That's a really good way to do it. I need pay more attention to the doc.thanks again


Return to “STM32 Support”

Who is online

Users browsing this forum: No registered users and 18 guests