Atualmente, estamos usando o microcontrolador PIC32 de 32 bits. Está funcionando bem para as nossas necessidades, mas também estamos explorando outros microcontoladores que podem nos acomodar melhor + temos outros projetos para os quais estamos selecionando o MCU. Para esse fim, selecionamos o microcontolador SAM DA baseado em ARM, que é o mesmo de 32 bits, mas baseado em ARM (mais popular que o PIC32 - na indústria).
Agora, para o PIC32, usamos o MPLAB, mas para o ARM córtex-M0, usaremos o Atmel Studio. Usaremos a linguagem C nas duas plataformas. O que me preocupa é que estaremos usando dois microcontoladores de 32 bits (da mesma empresa), mas com arquiteturas diferentes. Isso exigirá que aprendamos dois dispositivos diferentes e aumentará nossa "curva de aprendizado" + tempo de entrega. Mas, por outro lado, também acho que, como usaremos a linguagem C nos dois casos, a curva de aprendizado do ARM não deve ser tão ouvida e vale a pena explorar esse processador também.
Minha principal pergunta é: qual a diferença da arquitetura quando estamos programando em linguagem C, uma vez que fornece uma abstração das partes internas do microcontrolador. E quais são as principais diferenças no MPLAP e no Atmel Studio , considerando a programação em linguagem C?
fonte
Respostas:
Este é um tópico bastante opinativo. Eu posso falar por mim mesmo (AVR, ARM, MSP430).
A diferença 1 (mais significativa) está nos periféricos. Cada um dos MCU possui UART, SPI, temporizadores etc. semelhantes - apenas registre nomes e bits são diferentes. Na maioria das vezes, era o principal problema que eu tinha que lidar ao mover o código entre os chips. Solução: escreva seus drivers com uma API comum, para que seu aplicativo possa ser portátil.
A diferença 2 é a arquitetura da memória. Para colocar constantes em flash em um AVR, é necessário usar atributos e funções especiais para lê-las. No mundo do ARM, você simplesmente desrefere um ponteiro porque existe um único espaço de endereço (não sei como os PICs pequenos lidam com ele, mas presumiríamos que eles estão mais próximos do AVR).
A diferença 3 é declaração e manuseio de interrupção.
avr-gcc
tem aISR()
macro O ARM possui apenas um nome de função (como someUART_Handler () - se você usar cabeçalhos CMSIS e código de inicialização). Os vetores de interrupção ARM podem ser colocados em qualquer lugar (incluindo RAM) e modificados em tempo de execução (muito útil se você tiver, por exemplo, dois protocolos UART diferentes que podem ser alternados). O AVR tem apenas a opção de usar vetores no "flash principal" ou na "seção do carregador de inicialização" (portanto, se você quiser lidar com interrupções de maneira diferente, precisará usar umaif
instrução).Diferença 4 - modos de suspensão e controle de potência. Se você tiver a necessidade de menor consumo de energia, precisará executar todos os recursos do MCU. Isso pode diferir muito entre o MCU - alguns têm modos de economia de energia mais grosseiros, outros podem ativar / desativar periféricos individuais. Alguns MCUs possuem reguladores ajustáveis para que você possa executá-los com tensão mais baixa em velocidade mais baixa etc. Não vejo uma maneira fácil de obter a mesma eficiência em um MCU (digamos) com 3 modos de potência global e outro com 7 modos de potência e controle de relógio periférico individual.
A coisa mais importante ao se preocupar com a portabilidade é dividir claramente seu código em partes dependentes de hardware (drivers) e independentes de hardware (aplicativo). Você pode desenvolver e testar o último em um PC comum com um driver simulado (por exemplo, console em vez de um UART). Isso me salvou muitas vezes, pois 90% do código do aplicativo estava completo antes que o hardware do protótipo saísse do forno de refluxo :)
Na minha opinião, o bom do ARM é a "monocultura" - disponibilidade de muitos compiladores (gcc, Keil, IAR ... para citar alguns), muitos IDEs gratuitos e com suporte oficial (pelo menos para NXP, STM32, Silicon Labs, Nordic), muitas ferramentas de depuração (SEGGER - especialmente Ozone, ULINK, OpenOCD ...) e muitos fornecedores de chips (eu nem vou começar a nomeá-los). O PIC32 é principalmente limitado ao Microchip (mas só importa se você não gostar das ferramentas deles.
Quando se trata de código C. É 99% o mesmo, uma
if
declaração é a mesma, um loop funciona da mesma maneira. No entanto, você deve se preocupar com o tamanho da palavra nativa. Por exemplo, umfor
loop em um AVR é mais rápido se você usaruint8_t
para o contador, enquanto no ARMuint32_t
é o tipo mais rápido (ouint32_t
). O ARM precisaria verificar o estouro de 8 bits toda vez que você usasse um tipo menor.A seleção de um MCU e / ou fornecedor em geral é principalmente sobre política e logística (a menos que você tenha restrições muito claras de engenharia, por exemplo: alta temperatura - use MSP430 ou Vorago). Mesmo que o aplicativo possa ser executado em qualquer coisa e apenas 5% do código (drivers) precise ser desenvolvido e suportado durante a vida útil do produto - ainda é um custo extra para a empresa. Todos os lugares em que trabalhei tinham um fornecedor e uma linha MCU favoritos (como "escolha qualquer Kinetis que você queira, a menos que haja uma boa razão para escolher algo diferente"). Também ajuda se você tiver outras pessoas para pedir ajuda; assim, como gerente, eu evitaria ter um departamento de desenvolvimento para 5 pessoas, onde todos usassem um chip totalmente diferente.
fonte
Eu usei vários MCUs de quatro fabricantes diferentes. O trabalho principal é sempre familiarizar-se com os periféricos.
Por exemplo, um UART em si não é muito complexo e acho a porta dos meus drivers facilmente. Mas a última vez que levei quase um dia para que os relógios, pinos de E / S interrompessem, ativassem etc., foram resolvidos.
O GPIO pode ser muito complexo. Bit-set, bit-clear, bit-toggle, Funções especiais ativar / desativar, tri-state. Em seguida, você obtém interrupções: qualquer extremidade, ascensão, queda, nível baixo, nível alto, autolimpeza ou não.
Além disso, existem I2C, SPI, PWM, Timers e mais duas dúzias de periféricos, cada um com seu próprio relógio e cada vez que os registros são diferentes com novos bits. Para todos, leva muitas horas lendo a folha de dados como definir qual bit em que circunstâncias.
O último fabricante teve muitos exemplos de código que eu achei inutilizáveis. Tudo foi abstraído. Mas quando eu o localizei, o código passou por seis! níveis de chamadas de função para definir um bit GPIO. Bom se você tiver um processador de 3GHz, mas não em um MCU de 48MHz. Meu código no final era uma única linha:
Eu tentei usar drivers mais genéricos, mas desisti. Em um MCU, você está sempre lutando com os ciclos de espaço e relógio. Descobri que a camada de abstração é a primeira a sair pela janela se você gerar uma forma de onda específica em uma rotina de interrupção chamada 10KHz.
Portanto, agora tenho tudo funcionando e pretendo NÃO mudar novamente, a menos que por um motivo muito, muito bom.
Todos os itens acima devem ser amortizados sobre quantos produtos você vende e o que economiza. Venda de um milhão: economizar 0,10 para mudar para um tipo diferente significa que você pode gastar 100.000 em horas de trabalho com software. Vendendo 1000, você tem apenas 100 para gastar.
fonte
OUTPUT_HI(n)
qual produzirá um código equivalente aGPIOD->bssr |= 0x400;
sen
for uma constante como 0x6A, mas chamar uma sub-rotina simples sen
for não constante. Dito isto, a maioria das APIs de fornecedores que eu vi variam entre medíocres e horríveis.Isso é mais uma opinião / comentário do que uma resposta.
Você não quer e não deve programar em C. O C ++, quando usado da maneira correta , é muito superior. (OK, devo admitir que, quando usado de maneira errada, é muito pior que C.) Isso limita você a chips que possuem um compilador C ++ (moderno), que é praticamente tudo suportado pelo GCC, incluindo o AVR (com algumas limitações, filo menciona os problemas de um espaço de endereço não uniforme), mas excluindo quase todos os PICs (o PIC32 poderia ser suportado, mas ainda não vi nenhuma porta decente).
Quando você está programando algoritmos em C / C ++, a diferença entre as opções mencionadas é pequena (exceto que um chip de 8 ou 16 bits estará em grande desvantagem quando você faz muita aritmética de 16, 32 ou mais bits). Quando você precisar do último pingo de desempenho, provavelmente precisará usar o assembler (o seu ou o código fornecido pelo fornecedor ou por terceiros). Nesse caso, convém considerar novamente o chip selecionado.
Ao codificar para o hardware, você pode usar alguma camada de abstração (geralmente fornecida pelo fabricante) ou escrever sua própria (com base na folha de dados e / ou código de exemplo). As abstrações C existentes no IME (mbed, cmsis, ...) geralmente são funcionais (quase) corretas, mas falham horrivelmente no desempenho (verifique os antiquários discursam cerca de 6 camadas de indireção para uma operação de conjunto de pinos), usabilidade e portabilidade. Eles desejam expor toda a funcionalidade do chip em particular, o que em quase todos os casos você não precisará e nem se importa, e bloqueia seu código para esse fornecedor em particular (e provavelmente para esse chip).
É aqui que o C ++ pode fazer muito melhor: quando feito corretamente, um conjunto de pinos pode passar por 6 ou mais camadas de abstração (porque isso possibilita uma interface melhor (portátil!) E um código mais curto), além de fornecer uma interface independente do alvo para os casos simples , e ainda resultar no mesmo código de máquina que você escreveria no assembler .
Um trecho do estilo de codificação que eu uso, que pode deixar você entusiasmado ou se afastar horrorizado:
Na realidade, existem mais algumas camadas de abstração. No entanto, o uso final do led, digamos que ativá-lo, não mostra a complexidade ou os detalhes do alvo (para um arduin uno ou um comprimido azul ST32, o código seria idêntico).
O compilador não se deixa intimidar por todas essas camadas e, como não há funções virtuais envolvidas, o otimizador enxerga tudo (alguns detalhes, omitidos, como ativar o relógio periférico):
Foi assim que eu o teria escrito no assembler - se eu tivesse percebido que os registros do PIO podem ser usados com deslocamentos de uma base comum. Nesse caso, provavelmente o faria, mas o compilador é muito melhor em otimizar essas coisas do que eu.
Então, até onde eu tenho uma resposta, é: escreva uma camada de abstração para o seu hardware, mas faça isso em C ++ moderno (conceitos, modelos) para não prejudicar seu desempenho. Com isso, você pode mudar facilmente para outro chip. Você pode até começar a desenvolver algum chip aleatório que tenha, que esteja familiarizado, que tenha boas ferramentas de depuração, etc. e adie a escolha final até mais tarde (quando você tiver mais informações sobre a memória necessária, a velocidade da CPU, etc.).
Uma das falhas do desenvolvimento incorporado da OMI é escolher o chip primeiro (é uma pergunta freqüente neste fórum: qual chip devo escolher ... A melhor resposta é geralmente: não importa.)
(editar - resposta a "Portanto, em termos de desempenho, C ou C ++ estaria no mesmo nível?")
Para as mesmas construções, C e C ++ são os mesmos. O C ++ possui muito mais construções para abstração (apenas algumas: classes, modelos, constexpr) que podem, como qualquer ferramenta, ser usada para o bem ou para o mal. Para tornar as discussões mais interessantes: nem todo mundo concorda com o que é bom ou ruim ...
fonte
Se bem entendi, você deseja saber quais recursos específicos da arquitetura da plataforma "surgem" no seu ambiente de linguagem C, tornando mais desafiador escrever código portátil e de manutenção em ambas as plataformas.
C já é bastante flexível por ser um "montador portátil". Todas as plataformas que você selecionou têm compiladores comerciais / GCC disponíveis que oferecem suporte aos padrões de linguagem C89 e C99, o que significa que você pode executar código semelhante em todas as plataformas.
Existem algumas considerações:
Algumas plataformas / compiladores podem ocultar essa "limitação" melhor que outras. Por exemplo, no AVR, você precisa usar macros específicas para ler os dados da ROM. No PIC24 / dsPIC também existem instâncias tblrd dedicadas disponíveis. No entanto, algumas partes também têm o recurso "visibilidade do espaço do programa" (PSVPAG) disponível, que permite mapear uma página do FLASH para a RAM, disponibilizando o endereçamento imediato de dados sem tblrd. O compilador pode fazer isso com bastante eficiência.
ARM e MIPS são Von Neumann, portanto possuem regiões de memória para ROM, RAM e periféricos compactados em 1 barramento. Você não notará nenhuma diferença entre ler dados da RAM ou "ROM".
Por outro lado, um PIC24 permite que as operações da ALU leiam e gravem dados diretamente via endereçamento indireto (mesmo com modificações no ponteiro ..). Isso tem algumas características de uma arquitetura como o CISC, portanto, uma instrução pode fazer mais trabalho. Esse design pode levar a núcleos de CPU mais complexos, relógios mais baixos, maior consumo de energia etc. Felizmente, para você, a peça já foi projetada. ;-)
Essas diferenças podem significar que um PIC24 pode ser operações de E / S wrt "mais exigentes" do que um chip ARM ou MIPS com freqüência similar. No entanto, você pode obter uma peça ARM / MIPS do clocker muito mais alta pelas mesmas restrições de preço / pacote / design. Acho que, em termos práticos, acho que muitos "aprendendo a plataforma" estão aprendendo o que a arquitetura pode ou não fazer, a rapidez com que um conjunto de operações será, etc.
Essas diferenças podem ser um pouco encapsuladas pelos drivers de dispositivo, mas no final o firmware incorporado possui um alto nível de acoplamento com o hardware, portanto, às vezes, o trabalho personalizado não pode ser evitado.
Além disso, se você estiver deixando os ecossistemas do Microchip / antigo Atmel, poderá descobrir que as peças do ARM exigem mais configuração para que possam funcionar. Quero dizer em termos de; habilitar relógios para periféricos, depois configurá-los e "habilitá-los", configure o NVIC separadamente etc. Isso é apenas parte da curva de aprendizado. Depois que você se lembrar de fazer todas essas coisas, na ordem certa, escrever drivers de dispositivo para todos esses microcontroladores parecerá bastante semelhante em algum momento.
fonte
Sim e não. Do ponto de vista dos programadores, você está ocultando idealmente os detalhes do conjunto de instruções. Mas, até certo ponto, isso já não é relevante, pois os periféricos, que são o objetivo principal de escrever o programa, não fazem parte do conjunto de instruções. Agora, ao mesmo tempo, você não pode simplesmente comparar as partes do flash 4096Byte nesses conjuntos de instruções, principalmente se estiver usando C, a quantidade de consumo do flash / memória é fortemente determinada pelo conjunto de instruções e pelo compilador, alguns nunca devem ver um compilador (tosse PIC tosse) devido à quantidade de desperdício desses recursos consumida pela compilação. Outros o consumo de flash é uma sobrecarga menor. O desempenho também é um problema ao usar um idioma de alto nível e questões de desempenho nos aplicativos MCU, para que possa fazer a diferença entre gastar US $ 3 por placa para o mcu ou US $ 1.
Se o objetivo é facilitar a programação (com o custo geral do produto), você deve poder baixar um pacote de desenvolvedores para o mcu, de modo que a arquitetura do conjunto de instruções seja algo que você nunca vê; portanto, se essa é sua principal preocupação, não é uma preocupação. Ainda lhe custa dinheiro, tanto quanto o custo do produto para usar essas bibliotecas, mas, o tempo de lançamento no mercado pode ser mais curto, acho que as bibliotecas levam mais tempo / trabalho para usar do que diretamente falando com os periféricos.
Conclusão: os conjuntos de instruções são a menor das suas preocupações, passe para problemas reais.
fonte