C/C++

Dois IDEs que gosto e dois que odeio

No princípio você descobre o Dev-C++ começa a programar e os bugs te atormentam. Não lembro quais são eles mas me fez procurar alternativas e encontrei o caso de amor:

Code::Blocks – O profissional

Feito para ser uma alternativa ao Dev-C++ já deixou de ser alternativa e se tornou regra. Evoluiu muito e tem cada vez mais agregado funcionalidades úteis.

Captura de tela de 2016-07-01 19-47-20 - 1.png

Como se pode ver na figura os Templates são variados e nos ultimos releases há uma busca pelo desenvolvimento embarcado se aproveitando das diversas plataformas suportadas pelo GCC e o SDCC.

O Code::Blocks suporta desde o começo muitos compiladores. No Windows o download já vem embutido o Mingw ou TDM-GCC.

Eu uso ele profissionalmente para desenvolvimento C/C++ para as plataformas ARM (STM32 ou Kinetis) e AVR bem como x86. Deu um pouco de trabalho a configuração inicial dele para STM32 mas foi gratificante e muito instrutivo.

Captura de tela de 2016-07-01 19-59-01.png

O que mais gosto nele é facilidade de configuração e início médio. Eu odeio programas lentos como se verá nos que odeio. Mas ainda há uma opção veloz …

Geany – O pequeno poderoso

Eu adoro o Geany. É o IDE perfeito para testes rápidos e desenvolvimentos descompromissados. Diferente do Code:Blocks ele suporta diversas linguagens e tenho usado muito ultimante para Python.

Apesar de ter a opção de criar projetos o que me atrai nele é justamente você poder criar um arquivo, digitar o código e botar para rodar diretamente sem maiores burocracias. Por isso é perfeito para testes.E ao fechá-lo e abri-lo posteriormente as abas estarão lá abertas novamente.  Na verdade um projeto é nada mais que organizar seus arquivos em pastas que são navegáveis no painel “Aquivos” ao lado. Se o projeto cresceu então junte tudo em uma pasta e trabalhe com ela.

Captura de tela de 2016-07-01 20-03-43.png

E o inicio é rapido!

Como eu gosto disto! Clicou abriu! E o terminal embaixo? Quebra um galhão. Com um suporte a debug  ele pode até substituir o Code::Blocks.

linux_dark_1-24_weblinux_dark_1-24

As caras são muitas ao Geany para todos os gostos. Acho bonito programar em escuro mas não consigo me acostumar.

Os que odeio

Eclipse – Lerdeza

Pelo amor de Deus … como demora esta coisa para iniciar. Fiquei injuriado quando a Freescale mudou sua IDE para variantes Eclipse. Clicava em debug e aquela coisa ficava pensando … pensando … indexando. Que horror. Por isso fiz um esforço e aprendi a configurar o Code::Blocks para ARMs.

Essa tendência de se fazer IDEs baseadas no Eclipse para mim foi um desastre. A Texas fez isso, a Freescale e a ST esta indo no caminho. Sim, sim sei que o Eclipse é muito versátil mas convenhamos muito lento.  Certeira foi a Atmel que partiu para uma solução Visual Studio.

plc2-2015-your-own-ide-4-638

E aquela burocracia para se criar um projeto???? O sujeito que programa para sistemas embarcados em Eclipse se aprender diretamente nele se torna dependente do mesmo; porque não terá a menor noção de como se programa para uma plataforma: que comandos, que bibliotecas, que caminhos, como usar os comandos de debug. Tudo isso fica escondido em trezentos cliques que da medo e preguiça de mexer.

Se você usa o Eclipse e não tem a menor idéia de como as coisas funcionam por favor veja neste post como se programa “na mão”  um STM32.

Descobri neste site http://www.ihateeclipse.com  que não sou o único!

Vim/VI – O Geek

Fui atrás de um screenshot deles no Google e o que acho? Curiosamente mais destas imagens que do próprio em questão. Isto já diz tudo:

vi-vim-cheat-sheet

Ai vem aquele moleque que quer ser diferente e se acha o maior por usar um desses.

A primeira instalação que fazia em algum Linux novo  – antigamente pois depois do ArchLinux nunca mais fiz instalação nova – era o MCEdit para evitar ter que editar arquivos de configurações nesta !@#$!. Aliás como se sai mesmo dele?  Era algo como ESC-Q!.

Eu não odeio o Terminal. Eu adoro o modo texto! Uso demais o Guake (terminal suspenso), mas perder meu tempo aprendendo esses atalhos se posso usar um IDE mais robusto não é comigo.

Fazer um esforço para aprender estes comandos para mim é uma perda de tempo se posso aprender muitas outras coisas mais úteis. De fato no momento estou fazendo um esforço para escrever robôs investidores em Forex e Bolsa e estou adorando o tema em torno de processos estocásticos. O Geany e o Metatrader estão sendo perfeitos para isso.

main-qimg-cf12b776c0e69ec6d30a47b0d90792d9

Geração dodói

Se você programa nestas coisas parabéns te admiro, mas não me pertube.

 

 

 

Ponteiros em 7 arquiteturas – de z80 a ARM

Eu admito que somente entendi ponteiros quando fiz a ponte entre código em C e Assembly. Ponteiros são complicados mesmo. É um disfarce de alto nível para o baixo nível da máquina. Ponteiros devem ser usados com cuidado em muitos aspectos; como no problema de alocação dinâmica e liberação da memória alocada, múltiplas instâncias do mesmo endereço, problemas de endianess, acessos não autorizados ou em áreas indesejadas …

Por curiosidade gerei o código assembly  do código em C abaixo para 7 processadores ou microcontroladores sendo eles: HCS08, STM8, Z80, X86-64,  ARM, ColdFire e AVR.

 int *a,*b;
 char *c;
 int i = 10;

void main(void)
 {

a = &i;
 b = a;
 *b = *b + 5;
 c = (char *) &i;
 *c = *c + 5;
 c = c + 5;

while (1);

}

E vamos aos códigos. É bom lembrar que diferenças irão ocorrer não somente pela arquitetura mas pelos compiladores usados. Um exemplo é o SDCC que para somar 5 não utilizou instrução de soma mas cinco incrementos para o Z80, provavelmente pela contagem de ciclos.

ColdFire – O mais limpo dos códigos

_main:
; main:
lea _i(a5),a0
move.l a0,_a(a5)
;
; 13: b = a;
;
move.l _a(a5),d0
move.l d0,_b(a5)
;
; 14: *b = *b + 5;
;
movea.l d0,a0
addq.l #5,(a0)
;
; 15: c = (char *) &i;
;
lea _i(a5),a0
move.l a0,_c(a5)
;
; 16: *c = *c + 5;
;
movea.l _c(a5),a0
mvs.b (a0),d0
addq.l #5,d0
move.b d0,(a0)
;
; 17: c = c + 5;
; 18:
; 19: while (1);
;
addq.l #5,_c(a5)
;
; 19: 1);
;
bra.l *+0</pre>

Z80 – Se aproveita de seus registradores de 16 Bits e acesso indireto

_main::
 61 ;ponteiros.c:9: a = &i;
ld de,#_i+0
ld (_a),de
 64 ;ponteiros.c:10: b = a;
ld hl,(_a)
ld (_b),hl
 67 ;ponteiros.c:11: *b = *b + 5;
ld hl,(_b)
push hl
ld c,(hl)
inc hl
ld b,(hl)
pop hl
inc bc
inc bc
inc bc
inc bc
inc bc
ld (hl),c
inc hl
ld (hl),b
 82 ;ponteiros.c:12: c = (char *) &i;
ld (_c),de
 84 ;ponteiros.c:13: *c = *c + 5;
ld hl,(_c)
ld a,(hl)
add a, #0x05
ld (hl),a
 89 ;ponteiros.c:14: c = c + 5;
ld hl,#_c
ld a,(hl)
add a, #0x05
ld (hl),a
inc hl
ld a,(hl)
adc a, #0x00
ld (hl),a
 98 ;ponteiros.c:16: while (1);
00102$:
jr 00102$

STM8 – O registrador x de 16 bits e as instruções de carga 16 bits ajudam muito


1 ; C Compiler for STM8 (COSMIC Software)
 2 ; Parser V4.10.2 - 02 Nov 2011
 3 ; Generator (Limited) V4.3.7 - 29 Nov 2011
 15 bsct
 16 0000 _i:
 17 0000 000a dc.w 10
 50 ; 7 main()
 50 ; 8 {
 52 switch .text
 53 0000 _main:
 57 ; 10 a = &i;
 59 ldw x,#_i
 60 ldw _a,x
 61 ; 11 b = a;
 63 ldw x,#_i
 64 ldw _b,x
 65 ; 12 *b = *b + 5;
 67 ldw x,_i
 68 addw x,#5
 69 ldw _i,x
 70 ; 13 c = (char *) &i;
 72 ldw x,#_i
 73 ldw _c,x
 74 ; 14 *c = *c + 5;
 76 ld a,_i
 77 add a,#5
 78 ld _i,a
 79 ; 15 c = c + 5;
 81 ldw x,#_i+5
 82 ldw _c,x
 83 L12:
 84 ; 17 while (1);
 86 jra L12
 140 xdef _main
 141 xdef _i
 142 switch .ubsct
 143 0000 _c:
 144 0000 0000 ds.b 2
 145 xdef _c
 146 0002 _b:
 147 0002 0000 ds.b 2
 148 xdef _b
 149 0004 _a:
 150 0004 0000 ds.b 2
 151 xdef _a
 171 end

HCS08 – Surpreso? Esse processador só tem um registrador index e um acumulador!


11: 
 12: a = &i;
 LDHX @i
 STHX a
 13: b = a;
 STHX b
 14: *b = *b + 5;
 LDHX ,X
 AIX #5
 TXA 
 PSHH 
 LDHX @i
 STA 1,X
 PULA 
 STA ,X
 15: c = (char *) &i;
 16: *c = *c + 5;
 ADD #5
 STA ,X
 17: c = c + 5;
 AIX #5
 STHX c
 L1E:
 19: while (1);
 BRA L1E
 21: }

X86-64 –

{
 push %rbp
 mov %rsp,%rbp

 a = &i;
 movq $0x0,0x0(%rip)  
 b = a;
 mov 0x0(%rip),%rax 
 mov %rax,0x0(%rip) 
 *b = *b + 5;
 mov 0x0(%rip),%rax 
  mov 0x0(%rip),%rdx 
 mov (%rdx),%edx
 add $0x5,%edx
 mov %edx,(%rax)
 c = (char *) &i;
 movq $0x0,0x0(%rip) 

 *c = *c + 5;
 mov 0x0(%rip),%rax 
 mov 0x0(%rip),%rdx 
 movzbl (%rdx),%edx
 add $0x5,%edx
 mov %dl,(%rax)
 c = c + 5;
 mov 0x0(%rip),%rax 
 add $0x5,%rax
 mov %rax,0x0(%rip) 

 while (1);
 jmp 65

ARM – É um RISC …


push {fp} ; (str fp, [sp, #-4]!)
add fp, sp, #0 
//a = &i;
ldr r3, [pc, #116] 
ldr r2, [pc, #116] 
str r2, [r3]
//b = a;
ldr r3, [pc, #104] 
ldr r3, [r3]
ldr r2, [pc, #104] 
str r3, [r2]
//*b = *b + 5;
ldr r3, [pc, #96] 
ldr r3, [r3]
ldr r2, [pc, #88] 
ldr r2, [r2]
ldr r2, [r2]
add r2, r2, #5
str r2, [r3]
//c = (char *) &i;
ldr r3, [pc, #72] 
ldr r2, [pc, #60] 
str r2, [r3]
//*c = *c + 5;
ldr r3, [pc, #60] 
ldr r3, [r3]
ldr r2, [pc, #52] 
ldr r2, [r2]
ldrb r2, [r2]
add r2, r2, #5
and r2, r2, #255 
strb r2, [r3]
//c = c + 5;
ldr r3, [pc, #28] 
ldr r3, [r3]
add r3, r3, #5
ldr r2, [pc, #16] 
str r3, [r2] 
//while (1);
b 80 

AVR – Infelizmente o processamento ficou todo em registradores


push r28
push r29
in r28, 0x3d ; 61
in r29, 0x3e ; 62 
// a = &i;
ldi r24, 0x00 ; 0
ldi r25, 0x00 ; 0
sts 0x0000, r25
sts 0x0000, r24
// b = a;
lds r24, 0x0000
lds r25, 0x0000
sts 0x0000, r25
sts 0x0000, r24
// *b = *b + 5;
lds r24, 0x0000
lds r25, 0x0000
lds r18, 0x0000
lds r19, 0x0000
mov r30, r18
mov r31, r19
ld r18, Z
ldd r19, Z+1 ; 0x01
subi r18, 0xFB ; 251
sbci r19, 0xFF ; 255
mov r30, r24
mov r31, r25
std Z+1, r19 ; 0x01
st Z, r18
// c = (char *) &i;
ldi r24, 0x00 ; 0
ldi r25, 0x00 ; 0
sts 0x0000, r25
sts 0x0000, r24
// *c = *c + 5;
lds r24, 0x0000
lds r25, 0x0000
lds r18, 0x0000
lds r19, 0x0000
mov r30, r18
mov r31, r19
ld r18, Z
subi r18, 0xFB ; 251
mov r30, r24
mov r31, r25
st Z, r18
// c = c + 5;
lds r24, 0x0000
lds r25, 0x0000
adiw r24, 0x05 ; 5
sts 0x0000, r25
sts 0x0000, r24

// while (1);
rjmp .+0

Afinal o que é um ponteiro? É simplesmente uma posição de memória que guarda um endereço que contém algum dado.

São dois endereços: Um é a variável que contém o dado, o outro contém o endereço desta variável.

Assim b = &i diz ao processador para guardar no endereço b o endereço de i. Logo b e i estão apontando para o mesmo dado. Mas b não é igual a i. A variável i é um endereço que contém um dado e o ponteiro b é um endereço que contém um endereço que contém um dado.

Veja em HCS08

// a = &i
LDHX @i
STHX a
Carrega o endereço de i em HX e posteriormente guarda o valor de HX em a

Veja que em STM8

// *b = *b + 5
67 ldw x,_i
68 addw x,#5
69 ldw _i,x

O compilador decidiu por não usar o ponteiro b para acessar i pois ele percebeu que b ainda contém o endereço de i e usou diretamente o endereço de i. Normalmente o correto seria carregar o valor de b em x e então indiretamente ‘(x)’ carregar o valor endereçado por x no acumulador e finalmente efetuar a soma.

Foi exatamente o que o Z80 iria fazer

ld hl,(_b)
push hl
ld c,(hl)
inc hl
ld b,(hl)
pop hl
inc bc
inc bc
inc bc
inc bc
inc bc
ld (hl),c
inc hl
ld (hl),b

Espero ter ajudado a compreender o que de fato é um ponteiro. Entretanto este post foi útil e agradável ao demonstrar as diferentes formas de acesso a memória em diferentes arquiteturas e principalmente as otimizações que compiladores diferentes fazem.

 

Sistemas Operacionais Modernos – [ Livros que você deveria ler ]

Inauguro esta série com este livro que qualquer programador de baixo nível (nível máquina) deveria ler. Sendo honesto é uma livro chato de se ler porém de grande valor. O autor Tanenbaum é aquele que Linus Torvalds no passado mandou aquela famosa mensagem sobre desenvolver um sistema livre e acabou por criar o Linux.

Já no começo do livro há uma narrativa bem humorada sobre a história do desenvolvimento de sistemas operacionais e suas definições. Um livro comum sobre a área apenas joga o assunto e nos trás a noção que tudo surgiu por mágica. Porém Tanenbaum de fato desenvolveu e acompanhou o desenvolvimento de sistemas operacionais e é honesto em nos mostrar que alguns algorítimos não surgiram do nada mas vieram para resolver um problema de usuários ‘espertinhos’ ou melhorar o desempenho de velhos leitores de fita por exemplo.

Em seguida cada parte de um sistema operacional é revelado com seus algorítimos, deficiências e vantagens. As vezes tanta honestidade te deixa confuso como por exemplo qual método de gerenciamento de processos é o melhor? Ou o que fazer com DeadLocks? E esse é o valor do Livro: Ao lê-lo todo você tem uma noção completa de um sistema operacional.

Eu li na biblioteca da faculdade um antigo que tinha como sistema de estudo o MS-DOS. Pulei este capitulo. Mas li o Amoeba e o Unix. Os atuais tem Windows, Linux e outros.

Ler este livro te elevará a níveis de conhecimentos preciosos como programador. Se você gosta de algorítimos, a história da informática, gosta de conhecer outras plataformas e desenvolve para sistemas embarcados este livro será de grande utilidade.

Ah! Esqueça PDF. Ele é um livro grande para ser lido inteiro. Compre o livro físico e leia com calma.

 

STM32 no Linux

Programar Kits ou microcontroladores STM32 no Linux é perfeitamente possível graças ao GNU ARM Toolchain para tanto é necessário instalar este e outros pacotes primos como veremos neste post. Será mostrado aqui a instalação destes pacotes e um simples programa blink sem bibliotecas de suporte.

Há vários toolchains disponíveis para uC ARMs alguns que se utilizam das syscall de algum SO como o Linux e outros que não esperam nenhum Sistema Operacional por trás, programar assim é chamado comumente como ‘baremetal’.

Para debugar e gravar um STM32 será utilizado o ST-Util e o OpenOCD.

Instalando os pacotes

GCC + GDB + OpenOCD

Eu utilizo o ArchLinux mas não poderia discriminar o Ubuntu.
Para o Ubuntu:

sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi
sudo apt-get install gdb-arm-none-eabi
sudo apt-get install openocd

ArchLinux users:

sudo pacman -S arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-gdb arm-none-eabi-newlib openocd stlink

Se tudo ocorreu corretamente teste a instalação abrindo um shell e digitando arm-none <TAB> isso fará o bash autocompletar a sequência e mostrar os executáveis instalados como nesta saída tipica:

arm-none-eabi-addr2line arm-none-eabi-gcc-4.7.4 arm-none-eabi-ld.bfd
arm-none-eabi-ar arm-none-eabi-gcc-ar arm-none-eabi-nm
arm-none-eabi-as arm-none-eabi-gcc-nm arm-none-eabi-objcopy
arm-none-eabi-c++ arm-none-eabi-gcc-ranlib arm-none-eabi-objdump
arm-none-eabi-c++filt arm-none-eabi-gcov arm-none-eabi-ranlib
arm-none-eabi-cpp arm-none-eabi-gdb arm-none-eabi-readelf
arm-none-eabi-elfedit arm-none-eabi-gdbtui arm-none-eabi-size
arm-none-eabi-g++ arm-none-eabi-gprof arm-none-eabi-strings
arm-none-eabi-gcc arm-none-eabi-ld arm-none-eabi-strip

Primeiro Programa

Antes de tudo vamos iniciar a configuração do vetor de interrupções, a routina de reset e alguns Defines para alguns registradores que iremos usar. Este programa foi feito para o kit STM32 Discovery F0. Com poucas modificações e entendendo o processo a migração para outros (M3/M4) será fácil.

O primeiro arquivo boot.h contém alguns typedefs e Defines para registradores de clock e GPIOs, declaração de váriaveis externas do arquivo de link, prototypes de funções e o vetor de interrupções (que neste caso só cobrirá as interrupções ARM). Também o Reset e SystemInit bem como as funções de tratamento de interrupções serão declaradas.

boot.h

// boot.h
#define uint32_t unsigned int
#define IO_Uint32 volatile unsigned int
typedef struct
{
IO_Uint32 ACR;
IO_Uint32 KEYR;
IO_Uint32 OPTKEYR;
IO_Uint32 SR;
IO_Uint32 CR;
IO_Uint32 AR;
IO_Uint32 RESERVED;
IO_Uint32 OBR;
IO_Uint32 WRPR;
} _FLASH;

typedef struct
{
IO_Uint32 CR;
IO_Uint32 CFGR;
IO_Uint32 CIR;
IO_Uint32 APB2RSTR;
IO_Uint32 APB1RSTR;
IO_Uint32 AHBENR;
IO_Uint32 APB2ENR;
IO_Uint32 APB1ENR;
IO_Uint32 BDCR;
IO_Uint32 CSR;
IO_Uint32 AHBRSTR;
IO_Uint32 CFGR2;
IO_Uint32 CFGR3;
IO_Uint32 CR2;
} _RCC;

typedef struct {
IO_Uint32 MODER;
IO_Uint32 OTYPER;
IO_Uint32 OSPEEDR;
IO_Uint32 PUPDR;
IO_Uint32 IDR;
IO_Uint32 ODR;
IO_Uint32 BSRR;
IO_Uint32 LCKR;
IO_Uint32 AFRL[2];
IO_Uint32 BRR;
} _GPIO ;

#define GPIOC ((_GPIO *) 0x48000800)
#define RCC ((_RCC *) 0x40021000)
#define FLASH ((_FLASH *) 0x40022000)

// Globais do arquivo do linker
extern void *_estack;
extern unsigned char _sidata;
extern unsigned char _sdata;
extern unsigned char _edata;
extern unsigned char _sbss;
extern unsigned char _ebss;
extern void main (void);

// Entry Point
void Reset (void);
// Default Interrupt Handler
void Default_Handler(void) __attribute__( ( interrupt ) );
static void HardFault_Handler( void )/* __attribute__( ( naked ) )*/;

__attribute__ ((section(".isr_vector")))
void (* const Vectors[])(void) = {
(void(*)(void)) &amp;_estack,
Reset,
Default_Handler, //NMI_Handler,
HardFault_Handler, //FaultHandler,
0,
0,
0,
0,
0,
0,
0,
Default_Handler, //SVC_Handler,
0,
0,
Default_Handler, //PendSV_Handler,
Default_Handler, //SysTick_Handler,
};

static void HardFault_Handler( void ){
__asm__("BKPT");
while (1);
}

#pragma weak _exit = Default_Handler
void Default_Handler(void) {
__asm__("BKPT");
while (1);
}

void SystemInit(void)
{
/* Reseta o RCC clock para  o padrão ------------*/
/* Set HSION bit */
RCC-&gt;CR |= (uint32_t)0x00000001;

/* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */
RCC-&gt;CFGR &amp;= (uint32_t)0x08FFB80C;

/* Reset HSEON, CSSON and PLLON bits */
RCC-&gt;CR &amp;= (uint32_t)0xFEF6FFFF;

/* Reset HSEBYP bit */
RCC-&gt;CR &amp;= (uint32_t)0xFFFBFFFF;

/* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
RCC-&gt;CFGR &amp;= (uint32_t)0xFFC0FFFF;

/* Reset PREDIV[3:0] bits */
RCC-&gt;CFGR2 &amp;= (uint32_t)0xFFFFFFF0;

/* Reset USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */
RCC-&gt;CFGR3 &amp;= (uint32_t)0xFFFFFE2C;

/* Reset HSI14 bit */
RCC-&gt;CR2 &amp;= (uint32_t)0xFFFFFFFE;

/* Desabilita todas as interrupções*/
RCC-&gt;CIR = 0x00000000;

FLASH-&gt;ACR = 0x00000011;

}
// O começo de tudo antes de main
__attribute__( ( naked ) ) void Reset (void) {

register unsigned char *src, *dst;

// Copia os dados da flash para a ram (const, static initialized variables)
src = &amp;_sidata;
dst = &amp;_sdata;
while(dst &lt; &amp;_edata) *dst++ = *src++;

// Inicializa o bss (C static variables non initialized)
dst = &amp;_sbss;
while(dst &lt; &amp;_ebss) *dst++ = 0;

SystemInit();
//__libc_init_array();
// ... and finally call main saving sp
__asm__("b main");

}

A função Reset inicializa a variáveis estáticas com zero, chama SystemInit e finalmente chama a função Main com um brunch para economizar RAM na pilha.

main.h

O segundo arquivo é o fluxo principal do programa onde esta a função Main. Este programa irá piscar os LEDs do kit nas portas PC9 e PC8. O tipo GPIO foi declarado anteriormente em boot.h.

#include "boot.h"

__attribute__( ( naked ) ) void main (void){

RCC-&gt;AHBENR |= ((unsigned int) 0x00080000); // Enable GPIOC otherwise a hardfault
GPIOC-&gt;MODER = 0x55555555; // Output mode for all PORTC

volatile register int i;

while (1) {

GPIOC-&gt;BSRR = 0x0000ffff;
for (i = 0; i &lt; 50000; i++); GPIOC-&gt;BSRR = 0xffff0000;
for (i = 0; i &lt; 50000; i++);

};

__asm__("b .");
};

Makefile e Link Script

Acabou? Não  Ainda faltam dois arquivos o MakeFile e o Script do Linker.

Este MakeFile é completo o que compilará, mostrará o tamanho do código, converterá ELF para BIN, irá gerar uma listagem em assembly e finalmente grava na flash o programa. Simplesmente execute o make no terminal de onde estão estes arquivos.

all:
arm-none-eabi-gcc -mcpu=cortex-m0 -gdwarf-2 -mthumb -nostdlib -mlittle-endian -Tlink.ld main.c -o main
arm-none-eabi-size main
arm-none-eabi-nm -S main
arm-none-eabi-objdump -d -M intel -S main &gt; bin.asm
arm-none-eabi-objcopy -O binary main main.bin
st-flash erase
st-flash --reset write main.bin 0x08000000
debug:
arm-none-eabi-gdb main -tui --eval-command="target extended-remote :4242"

E por fim o link  script. Lugar onde as posições de memória são definidos, o Entry Point e algumas globais usadas pelo Reset para inicializar a variáveis estaticas e constantes.

/* Entry Point */
ENTRY(Reset)

/* Highest address of the user mode stack */
_estack = 0x20001FFF; /* end of RAM */

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0; /* Only usefull if you use alloc, malloc */
_Min_Stack_Size = 0x800;

MEMORY
{
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
}

/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} &gt;FLASH

/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)

KEEP (*(.init))
KEEP (*(.fini))

. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} &gt;FLASH

/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} &gt;FLASH

.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } &gt;FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} &gt;FLASH

.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} &gt;FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} &gt;FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} &gt;FLASH

/* used by the startup to initialize data */
_sidata = LOADADDR(.data);

/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */

. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} &gt;RAM AT&gt; FLASH

/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)

. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} &gt;RAM

._user_heap_stack :
{
. = ALIGN(4);
PROVIDE ( end = . ); /* Necessary for newlibc */
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} &gt;RAM

/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}

.ARM.attributes 0 : { *(.ARM.attributes) }
}

Foi apresentado aqui como programar microcontroladores STM32 no Linux utilizando as ferramentas gratuitas disponíveis. Não utilizei o conjunto de bibliotecas oficiais da ST chamado Cube para demonstrar também como é um programar um uC sem este suporte.

Como evolução o leitor irá integrar o Cube e utilizar o CubeMX para facilmente inicializar o uC e partir para a programação em si.

Eu programo microcontroladores STM32 no Linux profissionalmente com estas ferramentas e integro tudo no Code:Blocks que é uma ótima alternativa aos ambientes caros como IAR e Keil. Eu não me adaptei muito aos derivados gratuitos do Eclipse porque não gosto de programas que demoram para inicializar …

EDIT: O WordPress vive mudando meu código postado transformando caracteres especiais em formato HTML Entity e não sei como mudar isso. Se alguém ai souber me ajude por favor porque se você copiar e colar os códigos daqui será necessário corrigir muita pontuação.

Como funcionam os controles Rolling Codes/Hopping Code

No princípio a inocência

Houve uma época em que acionar o alarme de um carro era feito através do envio de um código de tamanho e forma fixa. O famoso e muito utilizado HT6P20-B da Holtek transmite  28 bits imutáveis a cada aperto do botão do controle.
O receptor era então sincronizado com este código e toda vez que este recebesse o código ele efetuava uma ação como travar/destravar o alarme, abrir e fechar o portão ou ligar uma lampada.

E os ladrões viraram nerds …

Enfim, um código fixo imutável poderia ser facilmente copiado e retransmitido por qualquer outro transmissor que o receptor nem saberia de quem se tratava. Mais conhecido como Clonagem ou Replay Attack.

Assim é o funcionamento do controle Learning Code.

Criptografia rolante

Uma forma de mudar isto era obviamente criptografar a informação – o ID do controle – . Mas se você pegar um código que nunca muda e criptografa-lo o resultado sera somente outro código fixo.

Para isso é necessário que se mude o código transmitido a cada aperto do botão ou tempo programado de forma que um possível intruso (homem do meio) não consiga prever qual o próximo código a ser gerado.

Para que o código varie constantemente normalmente é usado um contador interno que é  incrementado a cada aperto do botão. Assim monta-se uma bloco contendo o contador e algumas outras informações como o botão apertado e o nível da bateria do controle e criptografa-se este bloco com algum algoritmo criptográfico.

Como o contador constantemente varia o bloco transmitido será sempre diferente. Assim somente o receptor com a chave para descriptografar conseguirá ler o conteúdo.

E aqui fica o pulo do gato. Mesmo que um ladrão pegue uma transmissão legítima e reenvie ao receptor este verá que o contador desta transmissão já foi aceito antes, então ignora a transmissão. O receptor só ira aceitar uma transmissão válida se este bloco conter um contador inédito.

Veja que uma forma de ataque é poluir o espectro RF para que sua transmissão não chegue ao receptor – portão, carro – e o atacante consiga pegar esta transmissão e reenviar posteriormente ao receptor para efetuar a ação. Como o contador não foi recebido o receptor aceitará a transmissão. Por isso é bom certificar-se que o carro ‘travou’ o alarme do que simplesmente apertar o botão e nem dar bola.

Keeloq

Keeloq é um algoritmo proprietário da Microchip que criptografa um bloco de 32 bits com uma chave de 64 bits. Os chips mais famosos no Brasil atualmente utilizando ele são os HCS200 e HCS301 que pré-programados com a chave transmitem um código que sempre varia devido a um contador que é incrementado a cada aperto do botão.

As empresas tem uma chave única para todos os controles (ou segmentos de produtos, região). Se você descobrir a chave você clona qualquer carro ou portão do mesmo alarme.

AES128

Alguns estão começando a implementar um sistema rolling code com o AES128. A tendência é utilizar a mudança do contador de forma temporal ( a cada 1ms por exemplo).

A ideia é simples e de fácil implementação em um Arduino, para testar dois Arduinos e um par TX-RX são necessários. Um envia o código criptografado e o outro descriptografa, verifica e aciona:

Seja:
uint32 Contador
uint24 ID
uint4 Botão
Faça a cada 1ms:
Incremente o contador (ou outra forma de variação)
Se o botão for apertador faça:
Monte um bloco de 128 bits  contendo o contador, o botão e se quiser o ID.
Criptografe este bloco com a chave
Some a este bloco o ID (logo será um bloco de 24+128bits) e transmita

Clonagem

Se você pretende clonar algum controle Keeloq boa sorte. Há uma técnica utilizando o ataque de sub-channel onde é observado o consumo de energia de um receptor implementado em PIC e conhecidos os consumos específicos de cada instrução é possível estimar a chave. Porém é uma técnica avançada.

Para saber mais (todos em inglês) …

Cores no terminal serial pelo Arduino/STM32 no linux

Poucos sabem mas as cores em um terminal serial é possível devido a emulação VT-100 nos terminais que aceitam códigos de escape com certas ações e dessa forma pode-se fazer saídas intuitivas e bonitas como da imagem destacada do programa WeeChat.

Esses códigos não somente estabelecem cores mas fornecem comandos úteis para montar telas estáticas (não rolantes) como as que se consegue com a biblioteca ncurses (e antiga conio.h), inclusive com o uso dos recursos de janelas da tabela ASCII a ncurses torna-se desnecessária.

Veja, então, exemplos já embutidos em printf para aprender como utilizar estes códigos. Qualquer outra função serial que envie uma string entre aspas funcionará também.

Todos os códigos iniciam com o ESC em octal 033.

printf("\033[2J"); // Apaga todo o terminal
printf("\033[H"); // Manda o cursor para o começo da tela
printf("\033[?25l"); // Esconde o cursor
printf("\033[48;5;4m"); // Estabelece a cor Azul
printf("\033[0m"); // Reseta as formatações estabelecidas

Também funciona em Python como no exemplo:

print("\033[2;0f") # Coloca o cursor na linha 2

Na internet há muito mais códigos disponíveis como em http://ascii-table.com/ansi-escape-sequences-vt-100.php entretanto você poderá ver algumas diferenças entre os sites no código de escape utilizado pois VT-100 é uma tecnologia antiga e foi utilizada em várias plataformas diferentes mudando alguns aspectos.

Neste site (http://misc.flogisoft.com/bash/tip_colors_and_formatting) há uma lista útil para códigos de escape de cores. O site foi feito com exemplos para o comando echo do terminal porém basta substituir o \e por \033 e funcionará da mesma forma.

No Windows utilize o famoso Putty para poder visualizar as cores.

Esses recursos são úteis para se livrar do velho conceito de tela rolante e podermos fazer telas avançadas para visualização de controle de informações bonitas e funcionais.

.text .data .bss entendendo essas seções

Estes três nomes costumam aparecer muito quando compilamos e ‘linkamos’ algum programa utilizando o gcc e tanto faz se for x86, avr ou arm estas seções estarão lá e muitos não sabem o que significam. Para entende-los um utilitário útil para compreender estas seções e  ver o tamanho de um programa é o ‘size‘ cuja saída é tipicamente como segue

 $ arm-none-eabi-size bin/Release/out.a
 text       data      bss       dec     hex     filename
 10920   4         5564     16488 4068   bin/Release/out.a
 

No exemplo foi utilizado o size do toolchain arm e cada número indica o tamanho da seção em bytes. Há uma versão especifica para cada plataforma como visto na lista:

  • x86:  size
  • arm-baremetal: arm-none-eabi-size
  • avr: avr-size

O único argumento é o arquivo ELF.

Vamos entender de vez o que cada seção nos diz.

.text

Esta seção é o que será gravado na flash do microcontrolador, ou seja, o executável e dados constantes. O programa estará armazenado nesta seção assim como variáveis declaradas const.

Assim o seguinte código será todo ele incluído na seção .text:

const int seno[] = {0,1,0,-1};
void func (void) {
//... faz algo
}
int main (void) {
return 1;
}

Esse programa compilado para x86/Linux terá um tamanho de:

$ gcc c.c
$ size a.out
   text	   data	    bss	    dec	    hex	filename
   1199	    552	      8	   1759	    6df	a.out

As outras seções estão cheias devido a inicialização imposta pelo compilador.

.data

Nesta seção as variáveis que precisam ser inicializadas com algum valor serão alocadas. No código exemplo:

float PI = 3.14f;
int Mil = 1000;

As variáveis precisarão ser alocadas na RAM (ou SRAM) e inicializadas com um valor que será copiado da Flash para a RAM. Dessa forma PI é um posição da memória RAM que antes da execução de ‘main’ será preenchida com o valor copiado da Flash de ‘3.14f’.

Isso já indica um desperdício de espaço visto que a flash será ocupada pelos valores de inicialização.

.bss

A mais incompreendida das seções é somente a seção onde as variáveis globais não inicializadas são alocadas. Neste caso no C/C++ estas variáveis serão inicializadas com o valor 0. Lembra-se? Nada mais é que um memzero em uma região da RAM delimitada pelas variáveis declaradas globais. Não confunda com variáveis declaradas localmente que podem estar no stack ou nos registradores (e se não forem inicializadas terão valores indefinidos).

Antes de main

Quem faz todas estas inicializações é algum código startup.s a ser compilado ou linkado junto ao código que nada mais faz que zerar uma região da memória na seção .bss  neste caso e copiar da Flash para a RAM no caso da seção .data.

Algumas vezes queremos (e eu já precisei) não inicializar uma variável global com 0 então para isto use o atributo

int valoraleatorio __attribute__ ((section ( .noinit ))) ;

Que instrui o compilador a deixar de fora a variável da inicialização com zeros.

Raspberry PI barato

Raspberry PI 2

Diferença entre programador de TI e de uC

Programadores de uC vivem um mundo diferenciado e esqueçido pelos profissionais de TI. Seus desafios são conseguir velocidade que caibam em pouca memória e no dispositivo mais barato possível. Quando o projeto fica grande ele logo precisará de um mini-SO disponível ou que ele mesmo faça. Quem programa em uC acostumou-se a programar sem instruções de divisão e até mesmo multiplicação. Ponto flutuante é um terror, mas se conseguir fazer operações de ponto flutuante em tempo hábil ele se sente orgulhoso. Ele sofre não somente pela falta de recursos no uC mas ele também sofre com as diversas formas caras e insacessíveis de se programar e de debugar um uC. O programadores de TI tem hoje linguagens gratuitas e seus programas rodam em qualquer máquina x86 disponível em casa. Roda até em máquinas diversas em VMs. Quem programa em uC depende de um dispositivo programador que normalmente é caro e difícil de achar. Algumas diferenças entre os dois são: Programadores de TI

  • O inglês é desejado mas não obrigatório.
  • Não estão nem ai para a velocidade de execução do programa.
  • Eles não pensam duas vezes em usar uma biblioteca para resolver algo mesmo que esta seja custosa.
  • Escrevem verdadeiros livros usando a notação de objetos como Object.GetContainer.GetItem.GetMicroItem. São Tantos níveis que até se perdem.
  • Têm  disponível gratuitamente todo o ambiente de programação para fazer algo comercial e ganhar dinheiro.
  • Vivem criando novos termos dos que já existiam antes.
  • Suas leituras se resumem a Métodos ágeis, SQL, UML e assuntos de alto nível que nem dizem respeito a programação.
  • Em geral o ambiente é estático onde o desafio é somente o projeto em si, ou seja, depois de aprender a programar a interface e o banco de dados sobra apenas o projeto em si.

Programadores de uC

  • Em contra partida o ambiente muda a cada projeto. Novos uC, novos recursos, novos registradores a se programar, e novos datasheets.
  • O inglês é obrigatório para se ler DataSheets.
  • Precisam ao menos de um pouco de conhecimento de eletrônica
  • É obrigatório o C talvez o C++ e se for bom o ASM.
  • Dependem da empresa onde trabalham para comprar os kits de desenvolvimento ou dispositivos debug. (isso esta mudando).
  • Queimam alguns uCs!
  • Alguns têm a responsabilidade de programar dispositivos que controlam coisas que podem matar alguém.
  • Memória é um item de luxo para a maioria.
  • Eles vivem lendo datasheets para usar algum dispositivo interno do uC ou externo.

Rapido exemplo usando pthread.

A biblioteca pthread é um meio rápido e confiável de se criar aplicações multi-threads. Neste post será demonstrado um programa simples usando pthread.

  1. A biblioteca esta disponível no header <pthread.h> que deverá estar inclusa na compilação usando #include <pthread.h>.
  2. Criar a estrutura pthread_t para cada thread usado. Se for somente um pthread_t thread; será o suficiente caso contrário um ponteiro ou array de pthread_t será necessário como em pthread_t *threads;
  3. Criar a função que ficará separada do processo principal com tipo void e parametro *void. Como no exemplo void *Tarefa (void *param);
  4. Já criamos a estrutura e a função. Podemos então criar o thread usando pthread_create. Ela leva quatro parametros o primeiro a estrutura que criamos em 2; o segundo pode ser ignorado e passado NULL, o terceiro o ponteiro para a função (Tarefa) e finalmente o parametro (*void) para esta função (Tarefa (*void param)). Ao chamar pthread_create a tarefa (o thread) começa executando.
  5. Pronto já temos uma tarefa rodando em paralelo. Mas se esta tarefa tiver um fim precisamos esperar que ela termine antes que o processo principal termine antes e para isso usamos int pthread_join(pthread_t thread, void **value_ptr); que fica esperando pela tarefa identificada por pthread_t thread terminar e se esta retornou algum valor este estara guardado em void **value_ptr. Tenha em mente que esta função bloqueia a tarefa que a chamou até que a tarefa esperada termine.

Finalmente o código fica:


#include <pthread.h>
#include <stdio.h>

 void *Tarefa(void *param)
 {

 int *tx = (int *)param;
 int Fim = (*tx) + 20;

for (;(*tx) < Fim; (*tx)++;); // Incrementa X vinte vezes
 return NULL; // A tarefa não deverá retornar nada

}

int main()
 {

int x = 0, y = 0;

// Mostra antes
 printf("x: %d, y: %d\n ", x, y);

 pthread_t thread;

 if(pthread_create(&thread, NULL, Tarefa, &x)) {

fprintf(stderr, "Não deu certo a criação do thread\n");
 return 1;

}

 if(pthread_join(thread, NULL)) {

fprintf(stderr, "Falhou esperando fim da tarefa.\n");
 return 2;

}

// E finalmente o depois
 printf("x: %d, y: %d \n", x, y);

return 0;

}

Gerando tabelas C/C++ no octave

Uma forma rápida de se fazer tabelas de consultas C/C++ usando o octave é:


printf ("{") , printf("%d,",v) , printf("}");

Onde v é um vetor com os valores.

Por exemplo para 1024 valores de senos até PI em 16 bits use:

octave:> p = (pi) / 1024
p = 0.0030679615757713
octave:> v = uint16(1024*sin([0:p:(pi)])) 
// Veja que para valores negativos não dará certo
v =  0  3  6  9  13  16  19  

Valores em hexadecimal?

printf ("{") , printf("0x%x,",v) , printf("}");