Umas das facilidades desta arquitetura é a facilidade em desenvolver aplicações envolvendo comunicação USB. Neste tutorial são mostrados os passos para implementar a comunicação USB via CDC (Communications Device Class). São usados a placa FRDM-KL25Z com sistema operacional Windows 10. Esta implementação foi testada com máquinas reais e virtuais com êxito. Este tutorial foi baseado em (MCUONECLIPSE, 2016).
OBS.: tentativas anteriores usando Linux falharam, é algo que está sendo investigado.
1. Criando projeto
Com o projeto já criado (veja em Criando um projeto) adicione o componente FSL_USB_stack. Durante o processo de adição o aplicativo irá perguntar sobre a adição de outros componentes necessários para a implementação, neste caso o aplicativo está pedindo para adicionar uma nova seção crítica ou usar uma já existente. Como é o primeiro componente então é possível escolher a seção CS1, como visto na Fig. 1. Nos próximos componentes é recomendado criar novas seções.
Figura 1. Adicionando seções críticas.
Fonte: Elaborado pelo autor.
Além do stack USB também é necessário adicionar os componentes WAIT e UTILITY. Após adicionar os componentes, a janela Components ficará como na fig. 2. A fig. 2 mostra os componentes adicionados e ressalta que existem erros de configuração no componente USB. Isto ocorre sempre que ele é adicionado e é necessário configurá-lo adequadamente.
Figura 2. Componentes do projeto.
Fonte: Elaborado pelo autor.
2. Configurando o Clock do MCU
Para acessar as configurações do clock do MCU é necessário executar dois cliques no MCU da janela Components, como mostrado na fig. 3.
Figura 3. configurações de clock.
Fonte: Elaborado pelo autor.
Na aba System oscillator 0, o usuário deve habilitar a opção System oscillator 0 e em seguida, na aba Clock source settings o usuário configurar o modo de funcionamento do clock do MCU. Rolando a página o usuário tem acesso às configurações de clock, onde o usuário deve alterar o modo do MCG para PEE e selecionar a frequência de saída para 96 MHz, como visto na fig. 4.
Figura 4. selecionando a frequência de saída.
Fonte: Elaborado pelo autor.
Fazendo estas seleções o Component Inspector vai exibir alguns erros na aba Clock configurations, onde o usuário deve selecionar as frequências adequadamente como mostrado na fig. 5.
Figura 5. configurações de clock.
Fonte: Elaborado pelo autor.
3. Configurando o componente USB
Executando um duplo clique no componente USB, o Component inspector exibe o componente USB em detalhes, como visto na fig. 6. A janela mostra, além de outra configurações, as classes de dispositivos USB que podem ser implementados com este componente. Também é mostrado o que falta ser configurado neste componente.
Figura 6. Componentes do projeto.
Fonte: Elaborado pelo autor.
Expandindo o objeto, como na fig. 6, o component inspector exibe a janela vista na fig. 7, a qual exibe aba de configurações. Nesta janela é necessário selecionar a CPU, que neste caso é a KL25Z48, ao selecionar a CPU os erros devem ser resolvidos.
Figura 7. Selecionando a CPU.
Fonte: Elaborado pelo autor.
Para finalizar a configuração da USB, é necessário habilitar o módulo. No component inspector, selecione o componente USB0, dentro do grupo USB1. Como mostrado na Fig. 8, habilite o módulo no combo Clock gate e na aba Clock Settings, no combo Module clock source selecione a opção PLL/FLL clock. Com estas configurações o PE exibirá, logo abaixo do combo anterior, a informação da frequência atribuída ao módulo USB.
Figura 8. Habilitando a porta USB.
Fonte: Elaborado pelo autor.
4. Adicionando o código
Com os componentes adicionados e configurados é necessário adicionar o código para processar os eventos gerados pelo driver CDC da USB. O trecho de código abaixo pode ser adicionado ao corpo do arquivo main.c, logo após os includes.
CÓDIGO
static uint8_t cdc_buffer[USB1_DATA_BUFF_SIZE];
static uint8_t in_buffer[USB1_DATA_BUFF_SIZE];
static void CDC_Run(void) {
int i, cnt = 0;
uint32_t val = 0;
unsigned char buf[16];
for (;;) {
while (CDC1_App_Task(cdc_buffer, sizeof(cdc_buffer)) == ERR_BUSOFF) {
/* device not enumerated */
WAIT1_Waitms(10);
}
if (CDC1_GetCharsInRxBuf() != 0 && CDC1_App_Task(cdc_buffer, sizeof(cdc_buffer)) == ERR_OK) {
i = CDC1_GetCharsInRxBuf();
i = 0;
while (i < sizeof(in_buffer) - 1
&& CDC1_GetChar(&in_buffer[i]) == ERR_OK) {
i++;
}
in_buffer[i] = '\0';
(void) CDC1_SendString((unsigned char*) "echo: ");
(void) CDC1_SendString(in_buffer);
UTIL1_strcpy(buf, sizeof(buf), (unsigned char*) "val: ");
UTIL1_strcatNum32u(buf, sizeof(buf), val);
UTIL1_strcat(buf, sizeof(buf), (unsigned char*) "\r\n");
(void) CDC1_SendString(buf);
val++;
} else {
WAIT1_Waitms(10);
cnt++;
if ((cnt % 1024) == 0) { /* from time to time, write some text */
(void) CDC1_SendString(
(unsigned char*) "Type some text and it will echo.\r\n");
CDC1_SendBlock((unsigned char*) "hello?\r\n",
sizeof("hello?\r\n") - 1);
}
}
}
}
Em seguida, na rotina main, deve ser adicionado a chamada ao procedimento que foi adicionado no trecho acima. O trecho abaixo mostra como o código da rotina main deve ficar.
CÓDIGO
int main(void)
/*lint -restore Enable MISRA rule (6.3) checking. */
{
/* Write your local variable definition here */
/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
PE_low_level_init();
/*** End of Processor Expert internal initialization. ***/
/* Write your code here */
/* For example: for(;;) { } */
CDC_Run();
/*** Don't write any code pass this line, or it will be deleted during code generation. ***/
/*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
#ifdef PEX_RTOS_START
PEX_RTOS_START(); /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
#endif
/*** End of RTOS startup code. ***/
/*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
for(;;){}
/*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/
Com o código adicionado, basta compilar a aplicação de descarregá-la na placa.
5. Testando o código
Antes de executar o código o usuário deve conectar a segunda USB ao computador para que o mesmo a identifique. Com a porta identificada o teste é realizado com o uso de algum programa terminal como mostra a fig. 8. Pode ser visto que existem duas portas, uma do OpenSda, usada para depuração e a segunda porta (COM4), criada pela execução da aplicação.
Figura 8. realizando a Conexão.
Fonte: Elaborado pelo autor.
Com a conexão aberta, a placa enviará a mensagem vista na fig. 9 dentro de alguns segundos, se o usuário não pressionar nenhuma tecla no PC. o Usuário pode digitar algumas teclas ou colar uma palavra e a placa receberá estes caracteres e enviará novamente para a tela do terminal.
Figura 9. Testando a aplicação.
Fonte: Elaborado pelo autor.
A explicação do código será feita em aula.
Bons projetos!!!
Referências:
MCUONECLIPSE. USB CDC with the FRDM-K64F, finally!, 2016. Disponível em : <https://mcuoneclipse.com/2014/10/25/usb-cdc-with-the-frdm-k64f-finally/>. Acesso em 10de Julho de 2016.
Autor: Júlio César M. Ruzicki Eng.