SPL: работаем с USB

    Краткое описание: В этом примере будет установлена связь между компьютером и микроконтроллером с помощью аппаратного USB.  

    Задача: Требуется написать с использованием библиотеки StdPeriph и ST USB Library код. Программа должна производить настойку аппаратного USB. Используя стандартные функции библиотеки ST USB Library прочитать данные посылаемые компьютером. Успешное чтение данных подтвердить включением индикационного светодиода.  

    Для реализации примера потребуется подготовить некоторые моменты: Файлы библиотек и визард для настройки тактирования

    1) Установить драйвер виртуального COM порта для микроконтроллера STM32F303RBT6, например VCP_V1.5.0 скачать его можно с сайта STMicroelectronics

    2) Добавить библиотеки и файлы для работы с USB в созданный ранее проект из примера "Создание проекта в Keil uVision", прописать к ним пути.  

    3) Произвести настройки тактирования микроконтроллера для работы с USB в файле system_stm32f30x.c.  Поможет в этом визард STM32F30x_Clock_Configuration_V1.0.0.

    4) Для отправки данных от компьютера микроконтроллеру воспользуйтесь терминалом Terminal 1.9b его легко найти и скачать в интернете.

    

    После установки драйвера плата в диспетчере устройств должна определятся следующим образом рисунок 1. Обратите внимание что если контроллер не прошит прошивкой для работы с USB, то устройство определяться не будут.

Рисунок 1

    Скачайте файлы библиотеки для USB (ссылка в начале примера). Создайте две папки: USBSource и USBLib. Через Manage Project Items (нажать правой кнопкой на папку Target 1) перенесите эти файлы в свой проект рисунок 2. 

Рисунок 2

    После добавления файлов папка проекта должна выглядеть как на рисунке 3.

Рисунок 3

    Теперь нужно прописать пути к директории с файлами библиотек пример на рисунке 4.

Рисунок 4

    Самое время произвести настройки тактирования: нужно выставить параметры в визарде STM32F30x_Clock_Configuration_V1.0.0 (данный файл лежит в тойже папки что и библиотеки в начале примера, там же и заранее сгенерированный файл с натройками) как на рисунке 5 и сгенерировать файл system_stm32f30x.c

 Рисунок 5

   Основной файл main. Здесь в функции main происходит инициализация периферии. Основная работы программы происходит в прерывании.    

#include "hw_config.h"
#include "usb_lib.h"
#include "usb_desc.h"
#include "usb_pwr.h"

int main(void)
{
    Set_System();
    Set_USBClock();
    USB_Interrupts_Config();
    USB_Init();

    while (1) {
    }
}
#ifdef USE_FULL_ASSERT
/*******************************************************************************
* Function Name : assert_failed
* Description : Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* Input : - file: pointer to the source file name
* - line: assert_param error line source number
* Output : None
* Return : None
*******************************************************************************/
void assert_failed(uint8_t* file, uint32_t line)
{

    while (1) {
    }
}
#endif

   Файл hw_config.c. В данном файле следует обратить внимание на конфигурацию переферии и прерываний.

#include "stm32_it.h"
#include "usb_lib.h"
#include "usb_prop.h"
#include "usb_desc.h"
#include "hw_config.h"
#include "usb_pwr.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
ErrorStatus HSEStartUpStatus;
USART_InitTypeDef USART_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
uint8_t USART_Rx_Buffer[USART_RX_DATA_SIZE];
uint32_t USART_Rx_ptr_in = 0;
uint32_t USART_Rx_ptr_out = 0;
uint32_t USART_Rx_length = 0;

uint8_t USB_Tx_State = 0;
static void IntToUnicode(uint32_t value, uint8_t* pbuf, uint8_t len);
/* Extern variables ----------------------------------------------------------*/

extern LINE_CODING linecoding;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name : Set_System
* Description : Configures Main system clocks & power
* Input : None.
* Return : None.
*******************************************************************************/
void Set_System(void)
{
    GPIO_InitTypeDef gpio; //Объявляем переменную gpio типа GPIO_InitTypeDef

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //Включаем тактовый сигнал системной шины
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //Включаем тактирование порта A
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE); //Включаем тактирование порта E
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); //Включаем тактирование порта C

    // Настройки ножек USB
    gpio.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_Mode = GPIO_Mode_AF;
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &gpio);
    // Настройки ножек светодиодов
    gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(GPIOC, &gpio);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_14);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_14);

    //Настройка внешних прерываний для USB
    EXTI_ClearITPendingBit(EXTI_Line18);
    EXTI_InitStructure.EXTI_Line = EXTI_Line18;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

/*******************************************************************************
* Function Name : Set_USBClock
* Description : Configures USB Clock input (48MHz)
* Input : None.
* Return : None.
*******************************************************************************/
void Set_USBClock(void)
{
    RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
}

/*******************************************************************************
* Function Name : Enter_LowPowerMode
* Description : Power-off system clocks and power while entering suspend mode
* Input : None.
* Return : None.
*******************************************************************************/
void Enter_LowPowerMode(void)
{
    /* Set the device state to suspend */
    bDeviceState = SUSPENDED;
}

/*******************************************************************************
* Function Name : Leave_LowPowerMode
* Description : Restores system clocks and power while exiting suspend mode
* Input : None.
* Return : None.
*******************************************************************************/
void Leave_LowPowerMode(void)
{
    DEVICE_INFO* pInfo = &Device_Info;

    /* Set the device state to the correct state */
    if (pInfo->Current_Configuration != 0) {
        /* Device configured */
        bDeviceState = CONFIGURED;
    }

    else {
        bDeviceState = ATTACHED;
    }

    /*Enable SystemCoreClock*/
    SystemInit();
}

/*******************************************************************************
* Function Name : USB_Interrupts_Config
* Description : Configures the USB interrupts
* Input : None.
* Return : None.
*******************************************************************************/
// Настройка прерываний
void USB_Interrupts_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_Init(&NVIC_InitStructure);
}

/*******************************************************************************
* Function Name : USB_Cable_Config
* Description : Software Connection/Disconnection of USB Cable
* Input : None.
* Return : Status
*******************************************************************************/
void USB_Cable_Config(FunctionalState NewState)
{
}

/*******************************************************************************
* Function Name : USART_Config_Default.
* Description : configure the EVAL_COM1 with default values.
* Input : None.
* Return : None.
*******************************************************************************/
void USART_Config_Default(void)
{
}

/*******************************************************************************
* Function Name : USART_Config.
* Description : Configure the EVAL_COM1 according to the line coding structure.
* Input : None.
* Return : Configuration status
TRUE : configuration done with success
FALSE : configuration aborted.
*******************************************************************************/
bool USART_Config(void)
{
    return 0;
}

/*******************************************************************************
* Function Name : USB_To_USART_Send_Data.
* Description : send the received data from USB to the UART 0.
* Input : data_buffer: data address.
Nb_bytes: number of bytes to send.
* Return : none.
*******************************************************************************/
void USB_To_USART_Send_Data(uint8_t* data_buffer, uint8_t Nb_bytes)
{
}

/*******************************************************************************
* Function Name : Handle_USBAsynchXfer.
* Description : send data to USB.
* Input : None.
* Return : none.
*******************************************************************************/
void Handle_USBAsynchXfer(void)
{

    uint16_t USB_Tx_ptr;
    uint16_t USB_Tx_length;

    if (USB_Tx_State != 1) {
        if (USART_Rx_ptr_out == USART_RX_DATA_SIZE) {
            USART_Rx_ptr_out = 0;
        }

        if (USART_Rx_ptr_out == USART_Rx_ptr_in) {
            USB_Tx_State = 0;
            return;
        }

        if (USART_Rx_ptr_out > USART_Rx_ptr_in) /* rollback */
        {
            USART_Rx_length = USART_RX_DATA_SIZE - USART_Rx_ptr_out;
        }

        else {
            USART_Rx_length = USART_Rx_ptr_in - USART_Rx_ptr_out;
        }

        if (USART_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {
            USB_Tx_ptr = USART_Rx_ptr_out;
            USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;

            USART_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;
            USART_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;
        }

        else {
            USB_Tx_ptr = USART_Rx_ptr_out;
            USB_Tx_length = USART_Rx_length;

            USART_Rx_ptr_out += USART_Rx_length;
            USART_Rx_length = 0;
        }

        USB_Tx_State = 1;
        UserToPMABufferCopy(&USART_Rx_Buffer[USB_Tx_ptr], ENDP1_TXADDR, USB_Tx_length);
        SetEPTxCount(ENDP1, USB_Tx_length);
        SetEPTxValid(ENDP1);
    }
}
/*******************************************************************************
* Function Name : UART_To_USB_Send_Data.
* Description : send the received data from UART 0 to USB.
* Input : None.
* Return : none.
*******************************************************************************/
void USART_To_USB_Send_Data(void)
{
}

/*******************************************************************************
* Function Name : Get_SerialNum.
* Description : Create the serial number string descriptor.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Get_SerialNum(void)
{
    uint32_t Device_Serial0, Device_Serial1, Device_Serial2;

    Device_Serial0 = *(uint32_t*)ID1;
    Device_Serial1 = *(uint32_t*)ID2;
    Device_Serial2 = *(uint32_t*)ID3;

    Device_Serial0 += Device_Serial2;

    if (Device_Serial0 != 0) {
        IntToUnicode(Device_Serial0, &Virtual_Com_Port_StringSerial[2], 8);
        IntToUnicode(Device_Serial1, &Virtual_Com_Port_StringSerial[18], 4);
    }
}

/*******************************************************************************
* Function Name : HexToChar.
* Description : Convert Hex 32Bits value into char.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
static void IntToUnicode(uint32_t value, uint8_t* pbuf, uint8_t len)
{
    uint8_t idx = 0;

    for (idx = 0; idx < len; idx++) {
        if (((value >> 28)) < 0xA) {
            pbuf[2 * idx] = (value >> 28) + '0';
        }

        else {
            pbuf[2 * idx] = (value >> 28) + 'A' - 10;
        }

        value = value << 4;

        pbuf[2 * idx + 1] = 0;
    }
}

    Файл usb_endp.c. Здесь в оброботчике транзакций void EP3_OUT_Callback(void), происходит обработка полученных данных от компьютера.

#include "usb_lib.h"
#include "usb_desc.h"
#include "usb_mem.h"
#include "hw_config.h"
#include "usb_istr.h"
#include "usb_pwr.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

/* Interval between sending IN packets in frame number (1 frame = 1ms) */
#define VCOMPORT_IN_FRAME_INTERVAL 5

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];
extern uint8_t USART_Rx_Buffer[];
extern uint32_t USART_Rx_ptr_out;
extern uint32_t USART_Rx_length;
extern uint8_t USB_Tx_State;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name : EP1_IN_Callback
* Description :
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void EP1_IN_Callback(void)
{
}

/*******************************************************************************
* Function Name : EP3_OUT_Callback
* Description :
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void EP3_OUT_Callback(void)
{
    uint16_t USB_Rx_Cnt;

    USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, USB_Rx_Buffer); //Прием данных

    if (USB_Rx_Buffer[0] == 48) //Гасим светодиоды при передачи байта: 0 (dec) = (48 в таблице ASCII)
    {
        GPIO_ResetBits(GPIOC, GPIO_Pin_6);
        GPIO_ResetBits(GPIOC, GPIO_Pin_7);
    }

    if (USB_Rx_Buffer[0] == 49) //Зажигаем зеленый светодиод при передачи байта: 1 (dec) = (49 в таблице ASCII)
    {
        GPIO_ResetBits(GPIOC, GPIO_Pin_6);
        GPIO_SetBits(GPIOC, GPIO_Pin_7);
    }

    if (USB_Rx_Buffer[0] == 50) //Зажигаем красный светодиод при передачи байта: 2 (dec) = (50 в таблице ASCII)
    {
        GPIO_ResetBits(GPIOC, GPIO_Pin_7);
        GPIO_SetBits(GPIOC, GPIO_Pin_6);
    }

    SetEPRxValid(ENDP3); //Включаем прием данных для конечной точки 3
}
/*******************************************************************************
* Function Name : SOF_Callback / INTR_SOFINTR_Callback
* Description :
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void SOF_Callback(void)
{
    static uint32_t FrameCount = 0;

    if (bDeviceState == CONFIGURED) {
        if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL) {
            /* Reset the frame counter */
            FrameCount = 0;

            /* Check the data to be sent through IN pipe */
            Handle_USBAsynchXfer();
        }
    }
}

    Скомпилируйте проект. Если компилятор найдет ошибку в файле usb_pwr.c не пугайтесь. Найдите и замените все SCB_SCR_SLEEPDEEP на SCB_SCR_SLEEPDEEP_Pos.

     Если проблем нет то прошейте контроллер. Перезагрузите плату CodeIN и проверьте диспетчер устройств на наличие появившегося COM порта. 

    В случае если плата правильно определяется компьютером, запускайте терминал. Произведите настройки терминала согласно рисунку 6 и попробуйте отправить команды.  

Рисунок 6

    ВНИМАНИЕ: Приведенный листинг кода не является единственной и оптимальной реализацией поставленной задачи. Пример опубликован с демонстрационной целью. Также автор во избежание переписывания чужих статей, пропускает теоретические основы необходимые для понимания примера, связывая это с тем что всю необходимую информацию можно найти в интернете.

Комментариев (0)

Написать комментарий

Имя *
E-mail
Введите комментарий *
Капча
29 + ? = 30