cancel
Showing results for 
Search instead for 
Did you mean: 

USB Printer with STM32

PeppersONLY
Associate III

How would I go about implementing a USB host for a usb printer. I would like to send things through my STM32 to be printed. I’ve seen adaptations or some work done on the USB printer class as a device but I am unsure how I would do this as a host? Any ideas or already implemented libraries?

11 REPLIES 11
Pavel A.
Super User

Which kind of libraries? Do you want to print simple text or graphics? Do you need to deal with fonts, formatting languages etc.? 

 

Well, im wondering if I need a special USB printer host class to send anything including text and graphics. Is there just a bulk endpoint I can send whatever print data to?

gbm
Principal

Why don't you start by reading USB printer class specification document?

You need to know something about the printer you want to use. Simple printers use simple protocol with usually one OUT endpoint and simple command language. Modern laser and inkjet printers are much more sophisticated and may be hard to interact with.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

Hi. So I started writing a Printer Host Class based of the already implemented CDC class. I so far have just implemented sending stuff through the Bulk OUT endpoint. I wanted to know if I am on the right track. Here is what I have so far. Or should I just use the already existing MSC and adapt it for printer to make it easy?

 

#ifndef __USBH_PRINTER_H
#define __USBH_PRINTER_H

#ifdef __cplusplus
extern "C" {
#endif

#include "usbh_core.h"

typedef struct _PRINTER_Process
{
  uint8_t 		interface;
  uint8_t 		InEp;
  uint8_t 		OutEp;
  uint8_t       InPipe;
  uint8_t       OutPipe;
  uint16_t 		InEpSize;
  uint16_t 		OutEpSize;
  uint8_t       *pTxData;
  uint32_t 		TxDataLength;
  uint8_t  		*pRxData;
  uint32_t 		RxDataLength;
  PRINTER_StateTypeDef  		state;
}
PRINTER_HandleTypeDef;

typedef enum
{
  PRINTER_IDLE = 0U,
  PRINTER_SEND_DATA,
  PRINTER_SEND_DATA_WAIT,
  PRINTER_ERROR
}
PRINTER_StateTypeDef;

extern USBH_ClassTypeDef  PRINTER_Class;
#define USBH_PRINTER_CLASS    &PRINTER_Class

USBH_StatusTypeDef USBH_PRINTER_IOProcess(USBH_HandleTypeDef *phost);
USBH_StatusTypeDef USBH_PRINTER_Init(USBH_HandleTypeDef *phost);


#ifdef __cplusplus
}
#endif

#endif
#include "usbh_printer.h"

static USBH_StatusTypeDef USBH_PRINTER_InterfaceInit(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_PRINTER_InterfaceDeInit(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_PRINTER_Process(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_PRINTER_ClassRequest(USBH_HandleTypeDef *phost);


USBH_ClassTypeDef  PRINTER_Class =
{
  "PRINTER",
  USB_PRINTER_CLASS,
  USBH_PRINTER_InterfaceInit,
  USBH_PRINTER_InterfaceDeInit,
  USBH_PRINTER_ClassRequest,
  USBH_PRINTER_Process
};

static USBH_StatusTypeDef USBH_PRINTER_InterfaceInit(USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status;
  uint8_t interface;
  PRINTER_HandleTypeDef *PRINTER_Handle;

  interface = USBH_FindInterface(phost, 0x07, 0x01, 0x02); //Get interface index for specified printer values

  if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) {
	  USBH_DbgLog("Cannot Find the interface for Printer Class.", phost->pActiveClass->Name);
	  return USBH_FAIL;
  }

  status = USBH_SelectInterface(phost, interface); //Change phost selected interface

  if (status != USBH_OK)
  {
	  return USBH_FAIL;
  }

  phost->pActiveClass->pData = (PRINTER_HandleTypeDef *)USBH_malloc(sizeof(PRINTER_HandleTypeDef));
  PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;

  if (PRINTER_Handle == NULL)
  {
	  USBH_DbgLog("Cannot allocate memory for Printer Handle");
	  return USBH_FAIL;
  }

  (void)USBH_memset(PRINTER_Handle, 0, sizeof(PRINTER_HandleTypeDef));

  if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U) != 0U){
	  PRINTER_Handle->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
	  PRINTER_Handle->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
  } else {
	  PRINTER_Handle->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
	  PRINTER_Handle->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
  }

  if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress & 0x80U) != 0U){
	  PRINTER_Handle->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
	  PRINTER_Handle->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
  } else {
	  PRINTER_Handle->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
	  PRINTER_Handle->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
  }

  PRINTER_Handle->OutPipe = USBH_AllocPipe(phost, PRINTER_Handle->OutEp);
  PRINTER_Handle->InPipe = USBH_AllocPipe(phost, PRINTER_Handle->InEp);

  (void)USBH_OpenPipe(phost, PRINTER_Handle->OutPipe, PRINTER_Handle->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, PRINTER_Handle->OutEpSize);
  (void)USBH_OpenPipe(phost, PRINTER_Handle->InPipe, PRINTER_Handle->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, PRINTER_Handle->InEpSize);

  (void)USBH_LL_SetToggle(phost, PRINTER_Handle->OutPipe, 0U);
  (void)USBH_LL_SetToggle(phost, PRINTER_Handle->InPipe, 0U);

  return USBH_OK;
}

USBH_StatusTypeDef USBH_PRINTER_InterfaceDeInit(USBH_HandleTypeDef *phost)
{
  PRINTER_HandleTypeDef *PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;

  if ((PRINTER_Handle->InPipe) != 0U) {
	  (void)USBH_ClosePipe(phost, PRINTER_Handle->InPipe);
	  (void)USBH_FreePipe(phost, PRINTER_Handle->InPipe);
	  PRINTER_Handle->InPipe = 0U;
  }

  if ((PRINTER_Handle->OutPipe) != 0U) {
	  (void)USBH_ClosePipe(phost, PRINTER_Handle->OutPipe);
	  (void)USBH_FreePipe(phost, PRINTER_Handle->OutPipe);
	  PRINTER_Handle->OutPipe = 0U;
  }

  if ((phost->pActiveClass->pData) != NULL) {
	  USBH_free(phost->pActiveClass->pData);
	  phost->pActiveClass->pData = 0U;
  }

  return USBH_OK;
}

static USBH_StatusTypeDef USBH_PRINTER_ClassRequest(USBH_HandleTypeDef *phost)
{
  phost->pUser(phost, HOST_USER_CLASS_ACTIVE);

  return USBH_OK;
}


USBH_StatusTypeDef USBH_PRINTER_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint32_t length)
{
  USBH_StatusTypeDef Status = USBH_BUSY;
  PRINTER_HandleTypeDef *PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;

  if ((PRINTER_Handle->state == PRINTER_IDLE))
  {
    PRINTER_Handle->pTxData = pbuff;
    PRINTER_Handle->TxDataLength = length;
    PRINTER_Handle->state = PRINTER_SEND_DATA;
    Status = USBH_OK;

#if (USBH_USE_OS == 1U)
    USBH_OS_PutMessage(phost, USBH_CLASS_EVENT, 0U, 0U);
#endif /* (USBH_USE_OS == 1U) */
  }

  return Status;
}

static USBH_StatusTypeDef USBH_PRINTER_Process(USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status = USBH_BUSY;
  USBH_StatusTypeDef req_status = USBH_OK;
  USBH_URBStateTypeDef URB_Status = USBH_URB_IDLE;
  PRINTER_HandleTypeDef *PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;

  switch (PRINTER_Handle->state) {

  case PRINTER_IDLE:
	  status = USBH_OK;
	  break;

  case PRINTER_SEND_DATA:
	  // submit one chunk
	  if (PRINTER_Handle->TxDataLength > PRINTER_Handle->OutEpSize)
		  USBH_BulkSendData(phost, PRINTER_Handle->pTxData, PRINTER_Handle->OutEpSize,
							PRINTER_Handle->OutPipe, 1U);
	  else
		  USBH_BulkSendData(phost, PRINTER_Handle->pTxData, PRINTER_Handle->TxDataLength,
							PRINTER_Handle->OutPipe, 1U);

	  PRINTER_Handle->state = PRINTER_SEND_DATA_WAIT;
	  break;

  case PRINTER_SEND_DATA_WAIT:
	  URB_Status = USBH_LL_GetURBState(phost, PRINTER_Handle->OutPipe);

	  if (URB_Status == USBH_URB_DONE)
	  {
		  if (PRINTER_Handle->TxDataLength > PRINTER_Handle->OutEpSize)
		  {
			  PRINTER_Handle->TxDataLength -= PRINTER_Handle->OutEpSize;
			  PRINTER_Handle->pTxData += PRINTER_Handle->OutEpSize;
			  PRINTER_Handle->state = PRINTER_SEND_DATA;
		  }
		  else
		  {
			  PRINTER_Handle->TxDataLength = 0;
			  PRINTER_Handle->state = PRINTER_IDLE;
			  USBH_PRINTER_TransmitCallback(phost);
		  }
	  }
	  else if (URB_Status == USBH_URB_NOTREADY)
	  {
		  // NAK
		  PRINTER_Handle->state = PRINTER_SEND_DATA;
	  }
	  else if (URB_Status == USBH_URB_ERROR || URB_Status == USBH_URB_STALL)
	  {
		  PRINTER_Handle->state = PRINTER_ERROR;
	  }
	  break;

  case PRINTER_ERROR:
	  req_status = USBH_ClrFeature(phost, 0x00U);

	  if (req_status == USBH_OK)
	  {
		  PRINTER_Handle->state = PRINTER_IDLE;
	  }
	  break;

  default:
	  break;
  }

  return USBH_OK;
}

USBH_StatusTypeDef USBH_PRINTER_Init(USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef Status = USBH_BUSY;

#if (USBH_USE_OS == 1U)
  osEvent event;

  event = osMessageGet(phost->class_ready_event, osWaitForever);

  if (event.status == osEventMessage)
  {
    if (event.value.v == USBH_CLASS_EVENT)
    {
#else
  while ((Status == USBH_BUSY) || (Status == USBH_FAIL))
  {
    /* Host background process */
    USBH_Process(phost);

    if (phost->gState == HOST_CLASS)
    {
#endif
      Status = USBH_OK;
    }
  }
  return Status;
}

USBH_StatusTypeDef USBH_PRINTER_IOProcess(USBH_HandleTypeDef *phost)
{
  if (phost->device.is_connected == 1U)
  {
    if (phost->gState == HOST_CLASS)
    {
      USBH_PRINTER_Process(phost);
    }
  }

  return USBH_OK;
}

__weak void USBH_PRINTER_TransmitCallback(USBH_HandleTypeDef *phost)
{
    UNUSED(phost);
}

 

FBL
ST Employee

Hi @PeppersONLY 

We do not provide a direct example for hosting a printer using the classic middleware library or USBX. However, if you plan to use the USBX stack, this thread might be helpful for your implementation.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.




Best regards,
FBL

Does this mean that for USBX this won’t work on bare metal only a RTOS?

Not specifically @PeppersONLY. This implementation should work on both bare metal and in RTOS context.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.




Best regards,
FBL

@PeppersONLY wrote:

I wanted to know if I am on the right track. 


As @gbm said, have you looked at the documentation for the printer(s) you're thinking of using?

No use diving into this Printer Host Class if your intended printer(s) don't use it !

You still haven't given any indication of what type(s) of printers you want to use ...

Also, what STM32 ?

 

How to write your question to maximize your chances to find a solution

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

I don't understand how I could use the USBX stack on bare metal? Doesn't USBX rely on the handling of a RTOS like Azure.