/*
* 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);
}