реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Пример 3ch АЦП и USB HID на libopencm3, Просто пример с 3-мя инжектированными каналами АЦП и USB HID
messad_el
сообщение Nov 11 2015, 13:39
Сообщение #1





Группа: Новичок
Сообщений: 4
Регистрация: 5-11-15
Из: Серпухов,МО
Пользователь №: 89 185



И так я просто решил поделится примером. Так как очень сложно найти более менее толковые и близкие к жизни примеры на библиотеке libopencm3. Где бы АЦП юзался по человечески и результат бы переводился из попугаев в нормальные вольты или даже градусы. Так же мало толковых и рабочих примеров с USB HID. Я очень долго мучался с этими двумя темами аж целых две или три недели biggrin.gif . И по этому результатом своей работы решил максимально поделиться.
Проект представляет из себя программу для МК STM32f103 которая в режиме сканирования получает результат с трех инжектированных каналов АЦП(встроенного температурного датчика, встроенного источника опорного напряжения и ноги GPIO(то есть внешнего канала)) по прерыванию окончания преобразования. Данные полученые с этих каналов преобразуются в нормальные человеческие величины и засовываются в пакет USB HID когда это нужно хосту. Варганил это всё я на плате OLIMEX P-103 так что к ноге с которой я беру данные подключена кнопка "WakeUp", и при нажатии на неё там появляется 3.3v опорное напряжение на этой плате такое же(на сколько я понял тыкаясь тестером ибо в документации я это не уловил).
Пример собирал из разных других примеров, в описании(коментарие в начала кода) они приведены может что-то сделал и не совсем разумно, но для общего понимания мне кажется пойдёт.
CODE
/*
* This example was created to showcase the work with usb hid in data mode "Interrupt Transfers",
* using the library libopencm3. And when it is necessary not only to give the host data,
* but to take away the packets and respond to them.
* In this case, the request from the host to our final point, we modify the part of the received packet as
* well as writes the value obtained from the ADC via the built-in temperature sensor.
* The modified packet is sent back to the host.
* When you create this example, use the following example:
* https://github.com/libopencm3/libopencm3-ex...jec_timtrig_irq
* https://github.com/libopencm3/libopencm3-ex...sb_hid/usbhid.c
* Author Andrei Zaitsev November 6, 2015.
*/

#include <stdlib.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/adc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/hid.h>
#include <libopencm3/cm3/nvic.h>

#define ENDPOINT_ADDRESS_IN (0x81)
#define ENDPOINT_ADDRESS_OUT (0x01)

#define LED_PORT GPIOC
#define LED_PIN GPIO12
#define VREF 3.3
#define MAX_PARROTS 4096.0
#define MAXPACKETSIZEIN 16
#define MAXPACKETSIZEOUT 8


static usbd_device *usbd_dev;
volatile uint16_t temperature_in_parrots = 0;
volatile uint16_t Vref_in_parrots = 0;
volatile uint16_t Vwakeup_in_parrots = 0;
float V25 = 1.41;
float Avg_Slope = 4.3e-3;
//float Vref = 3.0;

void my_delay(int del);
float parrots_to_real(float val);

void my_delay(int del)
{
int i;

for (i = 0; i < del; i++)
__asm__("nop");
}

const struct usb_device_descriptor dev_descr = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483,
.idProduct = 0x5710,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};

static const uint8_t hid_report_descriptor[] =
{
0x06, 0xFF, 0xFF, // 04|2 , Usage Page (vendordefined?)
0x09, 0x01, // 08|1 , Usage (vendordefined
0xA1, 0x01, // A0|1 , Collection (Application)
// Feature report
0x09, 0x06, // 08|1 , Usage (vendordefined)
0x09, 0x07, // 08|1 , Usage (vendordefined)
0x15, 0x00, // 14|1 , LogicalMinimum(0 for signed byte)
0x75, 0x0F, // 74|1 , Report Size(16) =field size in bits = 1 byte
0x95, 0x08, //_0x04, // 94|1:ReportCount
0xB1, 0x02, // B0|1: Feature report
0xC0 // C0|0 , End Collection
};


static const struct {
struct usb_hid_descriptor hid_descriptor;
struct {
uint8_t bReportDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) hid_report;
} __attribute__((packed)) hid_function = {
.hid_descriptor = {
.bLength = sizeof(hid_function),
.bDescriptorType = USB_DT_HID,
.bcdHID = 0x0100,
.bCountryCode = 0,
.bNumDescriptors = 1,
},
.hid_report = {
.bReportDescriptorType = USB_DT_REPORT,
.wDescriptorLength = sizeof(hid_report_descriptor),
}
};

static const struct usb_endpoint_descriptor hid_endpoints[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = ENDPOINT_ADDRESS_IN, //0x81
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = MAXPACKETSIZEIN,
.bInterval = 1,
},
{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = ENDPOINT_ADDRESS_OUT, //0x01
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = MAXPACKETSIZEOUT,
.bInterval = 1,
}};

const struct usb_interface_descriptor hid_iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0, /* no boot */
.bInterfaceProtocol = 0, /* user (no mouse, keyboard, etc...)*/
.iInterface = 0,

.endpoint = hid_endpoints,

.extra = &hid_function,
.extralen = sizeof(hid_function),
};

const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = &hid_iface
}};

const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0xC0,
.bMaxPower = 0x32,

.interface = ifaces,
};

static const char *usb_strings[] = {
"Kill Soft",
"HID - ADC Demo",
"DEMO",
};

/* Buffer used for control requests. */
uint8_t usbd_control_buffer[128];

//This function looks identical in all examples.
//And it is as I understand monitors all inbound hid-requests.
static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
void (**complete)(usbd_device *dev, struct usb_setup_data *req))
{
(void)complete;
(void)dev;

if((req->bmRequestType != ENDPOINT_ADDRESS_IN) ||
(req->bRequest != USB_REQ_GET_DESCRIPTOR) ||
(req->wValue != 0x2200))
return 0;

/* Handle the HID report descriptor. */
*buf = (uint8_t *)hid_report_descriptor;
*len = sizeof(hid_report_descriptor);

return 1;
} //

float parrots_to_real(float val)//функция переводчик из попугаев в реальные вольты.
{
float f = (float)val;
return (f / 4096.0 * VREF);
//return (VREF/MAX_PARROTS)*f;
}

//This callback that is executed when the endpoint "OUT" request arrives.
static void data_rx(usbd_device *dev, uint8_t ep)
{
(void)ep;
(void)dev;

char buf[MAXPACKETSIZEIN];
float f_temp = 0.0;
uint16_t i_temp = 0;

gpio_toggle(LED_PORT, LED_PIN);

//Here we read buffer from the endpoint.
int len = usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, MAXPACKETSIZEOUT);

/*
*Beyond that we perform with him the necessary operations.
*For example, I increased the first seven bytes per unit,
*and vosmoy recorded temperature obtained from the ADC.
*/
//конечно может не совсем одекватный способ запихать float в два байта, но для нашей точности и так сойдёт
f_temp = parrots_to_real(temperature_in_parrots);
i_temp = (uint16_t)(f_temp * 100.0); //voltage temperature sensor

buf[0] = (i_temp)&0xFF;
buf[1] = (i_temp)>>8;

f_temp = ((V25 - f_temp)/Avg_Slope + 25.0);
i_temp = (uint16_t)(f_temp * 100.0);//temperature

buf[2] = (i_temp)&0xFF;
buf[3] = (i_temp)>>8;

f_temp = parrots_to_real(Vref_in_parrots);
i_temp = (uint16_t)(f_temp * 100.0); //Vref

buf[4] = (i_temp)&0xFF;
buf[5] = (i_temp)>>8;

f_temp = parrots_to_real(Vwakeup_in_parrots);
i_temp = (uint16_t)(f_temp * 100.0);//WakeUp

buf[6] = (i_temp)&0xFF;
buf[7] = (i_temp)>>8;

buf[8] = 0;
buf[9] = 0;
buf[10] = temperature_in_parrots;
buf[11] = temperature_in_parrots>>8;
buf[12] = Vref_in_parrots;
buf[13] = Vref_in_parrots>>8;
buf[14] = Vwakeup_in_parrots;
buf[15] = Vwakeup_in_parrots>>8;

/*
*And if the end point of the "OUT" we were able to read something,
*then write the modified buffer in endpoint "IN".
*/
if (len)
{
usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, buf, MAXPACKETSIZEIN);
buf[MAXPACKETSIZEIN] = 0;
}
}

//In this function, configure the endpoints and callbacks.
static void hid_set_config(usbd_device *dev, uint16_t wValue)
{
(void)wValue;

usbd_ep_setup(dev, ENDPOINT_ADDRESS_IN, USB_ENDPOINT_ATTR_INTERRUPT, MAXPACKETSIZEIN, NULL);
usbd_ep_setup(dev, ENDPOINT_ADDRESS_OUT, USB_ENDPOINT_ATTR_INTERRUPT, MAXPACKETSIZEOUT, data_rx);

usbd_register_control_callback(
dev,
USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
hid_control_request);
}

static void irq_setup(void)
{
/* Enable the adc1_2_isr() routine */
nvic_set_priority(NVIC_ADC1_2_IRQ, 0);
nvic_enable_irq(NVIC_ADC1_2_IRQ);
}

static void timer_setup(void)
{
/* Set up the timer TIM2 for injected sampling */
uint32_t timer;

timer = TIM2;
rcc_periph_clock_enable(RCC_TIM2);

/* Time Base configuration */
timer_reset(timer);
timer_set_mode(timer, TIM_CR1_CKD_CK_INT,
TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
timer_set_period(timer, 0xFF);
timer_set_prescaler(timer, 0x8);
timer_set_clock_division(timer, 0x0);
/* Generate TRGO on every update. */
timer_set_master_mode(timer, TIM_CR2_MMS_UPDATE);
timer_enable_counter(timer);
}

static void adc_setup(void)
{

rcc_periph_clock_enable(RCC_ADC1);

/* Make sure the ADC doesn't run during config. */
adc_off(ADC1);

/* We configure everything for one single timer triggered injected conversion with interrupt generation. */
/* While not needed for a single channel, try out scan mode which does all channels in one sweep and
* generates the interrupt/EOC/JEOC flags set at the end of all channels, not each one.
*/
adc_enable_scan_mode(ADC1);
//adc_set_single_conversion_mode(ADC1);
/* We want to start the injected conversion with the TIM2 TRGO */
adc_enable_external_trigger_injected(ADC1,ADC_CR2_JEXTSEL_TIM2_TRGO);
/* Generate the ADC1_2_IRQ */
adc_enable_eoc_interrupt_injected(ADC1);
adc_set_right_aligned(ADC1);
/* We want to read the temperature sensor, so we have to enable it. */
adc_enable_temperature_sensor(ADC1);
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC);

adc_power_on(ADC1);

/* Wait for ADC starting up. */
my_delay(800000); /* Wait a bit. */

adc_reset_calibration(ADC1);
while ((ADC_CR2(ADC1) & ADC_CR2_RSTCAL) != 0);
adc_calibration(ADC1);
while ((ADC_CR2(ADC1) & ADC_CR2_CAL) != 0);
}

static void gpio_setup(void)
{
// rcc_peripheral_enable_clock(RCC_GPIOA);
// rcc_peripheral_enable_clock(LED_PORT);
gpio_set(GPIOC, GPIO11 | LED_PIN);
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO11 | GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG,GPIO0 );
}

//The function which collects peripheral initialization.
static int init_peripheral(void)
{
uint8_t channel_array[4];

rcc_clock_setup_in_hsi_out_48mhz();

rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPCEN);


timer_setup();
irq_setup();
adc_setup();
gpio_setup();

usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev_descr, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));

usbd_register_set_config_callback(usbd_dev, hid_set_config);

my_delay(0x80000);

gpio_clear(GPIOC, GPIO11);

/* Select the channel we want to convert. 16=temperature_sensor. */
channel_array[0] = 16;//temperature_sensor
channel_array[1] = 17;//Vref
channel_array[2] = 0;//Wake Up button
/* Set the injected sequence here, with number of channels */
adc_set_injected_sequence(ADC1, 3, channel_array);

return 1;
}

int main(void)
{
init_peripheral();

while (1)
usbd_poll(usbd_dev);
}

void adc1_2_isr(void)
{
/* Clear Injected End Of Conversion (JEOC) */
ADC_SR(ADC1) &= ~ADC_SR_JEOC;
temperature_in_parrots = adc_read_injected(ADC1,1);
Vref_in_parrots = adc_read_injected(ADC1,2);
Vwakeup_in_parrots = adc_read_injected(ADC1,3);
}


Прилагаю так же архив с проектом, собирал всё под линуксом без использования IDE(только Qt Creator как редакторо кода), но я думаю не сложно будет как надо и пово что надо. Так же в каталоге "prot_bho" програмка для чтения моих пакетов из железки под linux написана с использованием libusb, сварганил её на скорую руку из чужой утили так что не судите строгоПрикрепленный файл  adc_usb.zip ( 255.21 килобайт ) Кол-во скачиваний: 38
.
Надеюсь это кому-то принесёт пользу.
Go to the top of the page
 
+Quote Post
smalcom
сообщение Nov 11 2015, 14:29
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 292
Регистрация: 26-06-07
Пользователь №: 28 718



Цитата
результат бы переводился из попугаев в нормальные вольты или даже градусы

а это проблема?

Код
for (i = 0; i < del; i++)
__asm__("nop");

вы серьёзно?

Цитата
(void)complete;

можете использовать атрибут unused

Цитата
float parrots_to_real(float val)//функция переводчик из попугаев в реальные вольты.
{
float f = (float)val;
return (f / 4096.0 * VREF);

даже не знаю с чего начать. с приведения переменной float к типу float или с обилия float'ов в такой простой операции.
ниже смотрел в ускоренном режиме: не буду раздувать сообщение.
Go to the top of the page
 
+Quote Post
Эдди
сообщение Nov 11 2015, 14:38
Сообщение #3


Знающий
****

Группа: Участник
Сообщений: 825
Регистрация: 16-04-15
Из: КЧР, Нижний Архыз
Пользователь №: 86 250



Ужас-то какой! На кой черт флоаты использовать на камне, где нет аппаратной поддержки плавающей запятой?
Все делается гораздо проще!

P.S. И работа с USB-HID сильно упрощается, если эмулировать клавиатуру (можно хоть к планшету подключить — испытано в "боевых условиях").

Сообщение отредактировал Эдди - Nov 11 2015, 14:40
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Nov 11 2015, 15:29
Сообщение #4


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(messad_el @ Nov 11 2015, 18:39) *
Надеюсь это кому-то принесёт пользу.

Спасибо, посмотрим!


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
Tarbal
сообщение Nov 11 2015, 15:45
Сообщение #5


Профессионал
*****

Группа: Свой
Сообщений: 1 351
Регистрация: 21-05-10
Пользователь №: 57 439



Спасибо, интересно.

В свою очередь поделюсь находкой.
Инструкция как сделать HID устройство с использованием cube. Первые 7:30 минут можно пропустить -- пустой треп.
https://www.youtube.com/watch?v=XRocqTfUxbo
Go to the top of the page
 
+Quote Post
messad_el
сообщение Nov 12 2015, 06:16
Сообщение #6





Группа: Новичок
Сообщений: 4
Регистрация: 5-11-15
Из: Серпухов,МО
Пользователь №: 89 185



Спасибо гляну на досуге. А поповоду первых сообщений, спасибо что носом ткнули, это приведение float к float это остатки от предыдущих экспериментов, там раньше интовая прееменная была, недоглядел когда перделывал, перевод из попугаев в вольты, для меня как для человека который только недавно(совсем недавно) сел за МК и полез в АЦП было трудно сразу понять ибо ни где достаточно прозрачно не написано от чего эти попугаи, по поводу того что половину работы на хосте можно было сделать я знаю, но это пример и я специально всё запихнул сюда, а вдруг в какой-то задаче хоста вообще не будет, а нужно будет, цикл с нопами взять из сотен примеров. Тем не менее спасибо!
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 03:10
Рейтинг@Mail.ru


Страница сгенерированна за 0.02401 секунд с 7
ELECTRONIX ©2004-2016