Discuss here different C compiler set ups, and compiling executables for the ESP8266

User avatar
By captbill
#22301 Hi,
I want to introduce to you guys a small IDE for the Oberon language which targets MCU's. It is called Astrobe Oberon. The complete development environment is contained in one @5mb download. No compiler to configure with endless settings and tools to mess with. You install it with the Windows installer, open the "blink.mod" compile and flash, all automatically. Literally takes 5 minutes and you have a flashing led. This includes all libraries, IDE and everything for a full blown Arduino-like IDE for MCU's...with one difference...it is pure Oberon.

http://www.astrobe.com/default.htm

Oberon is the work of Prof. Nicholas Wirth, who wrote Pascal and Modula. Oberon is the ultimate mix of Pascal (which is much like C++) with the modular features of Modula.

These languages are seen as 'esoteric' and complex by most but this is not the case. Oberon is brilliantly simple. Think BASIC with object oriented features. The "class" structure is done away with for a simpler method. The code is amazingly compact, and get this: the "blink" bin file from Arduino is 12 TIMES larger than the bin file produced from Oberon (136 bytes, yes BYTES!). That alone should make you take a closer look at Oberon as a potential for these tiny mcu's.
Code: Select allMODULE Blinker;

(* ========================================================================= 
   Example ARM Cortex-M3 Oberon Program 
 
   Description:
     Led connected to P1.18 blinking approx. once per second

   Target:
     NXP LPC1768
     
   Tested on:
     ARM mbed
   
   References:
     NXP UM10360 LPC17xx User manual
     Oberon for Cortex-M3 Microcontrollers
   
   (c) 2012-2013 CFB Software   
   http://www.astrobe.com 
   
   ========================================================================= *)

IMPORT Main, MCU, SYSTEM, Timer;

PROCEDURE Run();
CONST
  (* led connected to pin P1.18 *)
  ledBit = {18};
VAR
  direction: SET;
BEGIN
  (* Set led pin as output by setting the direction bit *)
  SYSTEM.GET(MCU.FIO1DIR, direction);
  SYSTEM.PUT(MCU.FIO1DIR, direction + ledBit);
 
  WHILE TRUE DO
    SYSTEM.PUT(MCU.FIO1SET, ledBit);
    Timer.MSecDelay(500);
    SYSTEM.PUT(MCU.FIO1CLR, ledBit);
    Timer.MSecDelay(500)
  END
END Run;

BEGIN
  Run()
END Blinker.


Code: Select allOberon for Cortex-M3 Compiler v5.3.0
  compiling Blinker
 code generated = 136 bytes, data = 0 bytes


I am very excited about the potentials of this neat little mcu in the ESP8266. Unfortunately, they are not so user friendly yet. The IDE's are rather complex to configure. Getting a working "blinky", for me, has been a real challenge.

If only Astrobe Oberon could target the ESP8266 mcu. It shouldn't be to hard for those with the know-how. The MCU.mod for the LPC1769 (the TARGET specific code) is only @700 lines. Let me know if anyone is interested and I will answer questions the best I can.
Last edited by captbill on Fri Jul 03, 2015 12:16 am, edited 1 time in total.
User avatar
By captbill
#22308 Take a look at the entire I2c.mod. The entire i2c protocol implemented in three hundred and forty lines. Compiles INSTANTLY.

Code: Select allOberon for Cortex-M3 Compiler v5.3.0
  compiling Buffer
 code generated = 636 bytes, data = 1072 bytes


code generated = 636 bytes, data = 1072 bytes

Just look at it.

Code: Select allMODULE I2C;
(* ========================================================================= 
   Astrobe Library Functions for LPC176x I2C0 / I2C1 / I2C2
   Master Transmitter / Receiver mode using polling

   Target: LPC176x family microcontrollers
   
   Refs:
     NXP LPC176x/5x User Manual UM10360

   (c) 2010-2015 CFB Software   
   http://www.astrobe.com 
   
   ========================================================================= *)

IMPORT MCU, SYSTEM;

CONST
  I2C0* = 0;
  I2C1* = 1;
  I2C2* = 2;
 
  (* Control bits *)
  AA*  = 2;
  SI*  = 3;
  STO* = 4;
  STA* = 5;
  ENA* = 6;
 
  (* Result codes *)
  OK*    = 1;
  Error* = 0;
  Empty* = -1;
 
  (* Status codes *)
  StartTransmit*     = 008H;
  StartRepeat*       = 010H;
  SlaveWriteTxAck*   = 018H;
  SlaveWriteTxNoAck* = 020H;
  DataTxAck*         = 028H;
  DataTxNoAck*       = 030H;
  SlaveReadTxAck*    = 040H;
  SlaveReadTxNoAck*  = 048H;
  MasterRxAck*       = 050H;
  MasterRxNoAck*     = 058H;
  NoInfo*            = 0F8H;

TYPE
  ConfigurePinsProc* = PROCEDURE;
 
VAR
  I2CConSet: INTEGER;
  I2CStat: INTEGER; 
  I2CDat: INTEGER;
  I2CAdr: INTEGER;
  I2CSclh: INTEGER;
  I2CScll: INTEGER;
  I2CConClr: INTEGER;
 

PROCEDURE* GetStatus(VAR status: INTEGER);
BEGIN
  REPEAT UNTIL SYSTEM.BIT(I2CConSet, SI);
  SYSTEM.GET(I2CStat, status)
END GetStatus;


PROCEDURE DoStart(): INTEGER;
VAR
  result, status: INTEGER;
BEGIN
  result := 0;
  WHILE (result = 0) DO
    GetStatus(status);
    IF (status = StartTransmit) OR (status = StartRepeat) THEN
      result := OK
    ELSIF (status # NoInfo) THEN
      result := status
    ELSE
      SYSTEM.PUT(I2CConClr, {SI})
    END
  END;
  SYSTEM.PUT(I2CConClr, {STA});
  RETURN result
END DoStart;


PROCEDURE Start(): INTEGER;
BEGIN
  SYSTEM.PUT(I2CConSet, {STA});
  RETURN DoStart()
END Start;


PROCEDURE RepeatStart(): INTEGER;
BEGIN
  SYSTEM.PUT(I2CConSet, {STA});
  SYSTEM.PUT(I2CConClr, {SI});
  RETURN DoStart()
END RepeatStart;


PROCEDURE* Stop();
BEGIN
  SYSTEM.PUT(I2CConSet, {STO});
  SYSTEM.PUT(I2CConClr, {SI});
  WHILE SYSTEM.BIT(I2CConSet, STO) DO END
END Stop;


PROCEDURE* SendByte(c: BYTE);
BEGIN
  REPEAT UNTIL SYSTEM.BIT(I2CConSet, SI);
  SYSTEM.PUT(I2CDat, c);
  SYSTEM.PUT(I2CConClr, {SI})
END SendByte;


PROCEDURE* ReceiveByte(VAR b: BYTE);
BEGIN
  REPEAT UNTIL SYSTEM.BIT(I2CConSet, SI);
  SYSTEM.GET(I2CDat, b)
END ReceiveByte;
     

PROCEDURE* GenAck0;
VAR
  control: SET;
BEGIN
  SYSTEM.GET(I2CConSet, control);
  SYSTEM.PUT(I2CConSet, control + {AA});
  SYSTEM.PUT(I2CConClr, {SI})
END GenAck0;


PROCEDURE* GenAck1;
BEGIN
  SYSTEM.PUT(I2CConClr, {AA});
  SYSTEM.PUT(I2CConClr, {SI})
END GenAck1;


PROCEDURE Wait(): INTEGER;
VAR
  result, status: INTEGER;
BEGIN
  result := Error;
  REPEAT
    GetStatus(status);
    IF (status = SlaveWriteTxAck) OR (status = DataTxAck) THEN
      result := OK
    END
  UNTIL status # NoInfo;
  RETURN result
END Wait;
 

PROCEDURE WriteParams(addr: BYTE; params: ARRAY OF BYTE; psize: INTEGER): INTEGER;
VAR
  i, result: INTEGER;
BEGIN
  ASSERT((psize >= 0) & (psize <= LEN(params)), 25);
  result := Start();
  IF result = OK THEN
    SendByte(addr);
    result := Wait()
  END;
  i := 0;
  WHILE (i < psize) & (result = OK) DO 
    SendByte(params[i]);
    result := Wait();
    INC(i)
  END;
  RETURN result
END WriteParams;


PROCEDURE Ready*(addr: BYTE): INTEGER;
VAR
  result, status: INTEGER;
  ready: BOOLEAN;
BEGIN
  ASSERT(addr <= 07FH, 22);
  (* Writing: the LSB (R/W) bit is zero *)
  addr := LSL(addr, 1);
  ready := FALSE;
  result := OK;
  WHILE ~ready DO
    result := Start();
    IF result = OK THEN
      SendByte(addr);
      GetStatus(status);
      IF status = SlaveWriteTxAck THEN
        ready := TRUE
      ELSIF status = SlaveWriteTxNoAck THEN
        ready := FALSE
      ELSIF status # NoInfo THEN
        result := Error;
        ready := TRUE
      END
    END;
    Stop()
  END;
  RETURN result
END Ready;


PROCEDURE WriteBytes*(addr: BYTE; params: ARRAY OF BYTE; psize: INTEGER;
  data: ARRAY OF BYTE; offset, count: INTEGER): INTEGER;
VAR
  result, i, limit: INTEGER;
BEGIN
  limit := offset + count;
  ASSERT(addr <= 07FH, 21);
  ASSERT((count >= 0) & (limit <= LEN(data)), 26);
  (* Writing: the LSB (R/W) bit is zero *)
  addr := LSL(addr, 1);
  result := WriteParams(addr, params, psize);
  i := offset;
  WHILE (i < limit) & (result = OK) DO 
    SendByte(data[i]);
    result := Wait();
    INC(i)
  END;
  Stop();
  RETURN result
END WriteBytes;
 

PROCEDURE Write*(addr: BYTE; params, data: ARRAY OF BYTE): INTEGER;
  RETURN WriteBytes(addr, params, LEN(params), data, 0, LEN(data))
END Write;


PROCEDURE ReadByte(VAR b: BYTE; lastByte: BOOLEAN): INTEGER;
VAR
  status, result: INTEGER;
BEGIN
  result := Empty;
  WHILE (result = Empty) DO 
    GetStatus(status);
    IF (status = SlaveReadTxAck) OR (status = SlaveReadTxNoAck) OR (status = MasterRxAck) THEN
      IF lastByte THEN
        GenAck1()
      ELSE
        GenAck0()
      END;
      ReceiveByte(b);
      result := OK
    ELSIF (status # NoInfo) THEN
      result := Error
    END
  END;
  RETURN result
END ReadByte;


PROCEDURE ReadBytes*(addr: BYTE; params: ARRAY OF BYTE; psize: INTEGER;
      VAR data: ARRAY OF BYTE; offset, count: INTEGER): INTEGER;
VAR
  result, i, limit: INTEGER;
  lastByte: BOOLEAN;
BEGIN
  limit := offset + count;
  ASSERT(addr <= 07FH, 20);
  ASSERT((count >= 0) & (limit <= LEN(data)), 27);
 
  (* Send the read parameters *)
  (* Writing: the LSB (R/W) bit is zero *)
  addr := LSL(addr, 1);
  result := WriteParams(addr, params, psize);
 
  (* Receive the data sent back *)
  IF result = OK THEN
    result := RepeatStart()
  END;
  (* Reading: set the R/W bit *)
  IF result = OK THEN
    SendByte(addr + 1)
  END;
  i := 0;
  WHILE (i < limit) & (result = OK) DO
    lastByte := (i = limit - 1);
    result := ReadByte(data[i], lastByte);
    INC(i)
  END;
  Stop()
  RETURN result
END ReadBytes;


PROCEDURE Read*(addr: BYTE; params: ARRAY OF BYTE; VAR data: ARRAY OF BYTE): INTEGER;
  RETURN ReadBytes(addr, params, LEN(params), data, 0, LEN(data))
END Read;

 
PROCEDURE* SetBus(bus: INTEGER);
VAR
  I2CBase: INTEGER;
BEGIN
  CASE bus OF
    0: I2CBase := MCU.I2C0Base
  | 1: I2CBase := MCU.I2C1Base
  | 2: I2CBase := MCU.I2C2Base
  END;
  I2CConSet := I2CBase;
  I2CStat   := I2CBase + 004H;
  I2CDat    := I2CBase + 008H;
  I2CAdr    := I2CBase + 00CH;
  I2CSclh   := I2CBase + 010H;
  I2CScll   := I2CBase + 014H;
  I2CConClr := I2CBase + 018H
END SetBus;


PROCEDURE Init*(bus: INTEGER; ConfigurePins: ConfigurePinsProc; freq: INTEGER);
VAR
  scl, sclh, scll: INTEGER;
BEGIN
  ASSERT(bus IN {I2C0, I2C1, I2C2}, 23);
  ASSERT((freq = 100000) OR (freq = 400000), 24);
  SetBus(bus);
  ConfigurePins();

  (* Clear flags *)
  SYSTEM.PUT(I2CConClr, {AA, SI, STA, ENA});

  (* Set the data rate and duty cycle *)
  scl := MCU.PCLK DIV freq;
  sclh := (scl + 1) DIV 2;
  scll := scl DIV 2;
  SYSTEM.PUT(I2CSclh, sclh);
  SYSTEM.PUT(I2CScll, scll);

  (* Enable I2C *)
  SYSTEM.PUT(I2CAdr, 0);
  SYSTEM.PUT(I2CConSet, {ENA})
END Init;

END I2C.

User avatar
By captbill
#22312 You want graphics? Here is the complete library which handles the drawing in this video...from 2009!!

https://www.youtube.com/watch?v=fQcMvLxUmVU
(What's the trick to embed video?)

22.2 KB!!

Code: Select allOberon for Cortex-M3 Compiler v5.3.0
  compiling LCDST756R

  Line  Col
    47   41 Warning: length rounded up
 code generated = 1732 bytes, data = 1044 bytes



Code: Select allMODULE LCDST756R;
(* ========================================================================= 
   Example Cortex-M3 Oberon Module 
 
   Description:
     Sitronix ST756R LCD Controller Driver

   Target:
     mbed systems
     
   Tested on:
     ARM mbed Application Board with a Newhaven C12832A1Z 128 x 32 LCD display
   
   Reference:
     Sitronix ST756R DataSheet Ver 1.5 (10 Mar 2006).
     
   (c) 2013-2014 CFB Software
   http://www.astrobe.com
   
   ========================================================================= *)

IMPORT Timer, MCU, ResData, SPI, SYSTEM;

CONST
  Black*   = 0;
  White*   = 1;
 
  MaxX* = 127;
  MaxY* = 31;
 
  FontWidth = 6;
  FontHeight = 8;
 
  Cols* = (MaxX + 1) DIV FontWidth;
  Rows* = (MaxY + 1) DIV FontHeight;
  Pages* = (MaxY + 1) DIV 8;
 
  A0 = {6};    (* P0.6  = mbed P8 *)
  CS = {18};   (* P0.18 = mbed P11 *)
  Reset = {8}; (* P0.8  = mbed P6 *)

TYPE
  (* Each bit represents a pixel on the screen *)
  (* Each page can be refreshed invidually *)
  BitMap = ARRAY MaxX + 1 OF SET;
  (* 6 x 8 pixels *)
  FontPattern = ARRAY FontWidth OF BYTE;
 
VAR
  font: ResData.Resource;
  fontPattern: FontPattern;
  (* In-memory representation of the screen *)
  bitMap0, bitMap: BitMap;
   

  PROCEDURE LoadFont*(name: ARRAY OF CHAR): BOOLEAN;
  BEGIN
    ResData.Open(font, name);
    RETURN ResData.Size(font) > 0
  END LoadFont;
   

  (* Store the font data for a character in a 2-D pixel array *)
  PROCEDURE CharToFontPattern(ch: CHAR; VAR fontPattern: FontPattern);
  VAR
    i, index: INTEGER;
  BEGIN
    IF (ORD(ch) < ORD(" ")) OR (ORD(ch) > ORD("~")) THEN ch := "." END;
    index := (ORD(ch) - ORD(" ")) * 8;
    FOR i := 0 TO FontWidth - 1 DO 
      ResData.GetByte(font, index + i, fontPattern[i]);
    END
  END CharToFontPattern;
 
   
  PROCEDURE SendData(data: INTEGER);
  BEGIN
    SYSTEM.PUT(MCU.FIO0SET, A0);
    SYSTEM.PUT(MCU.FIO0CLR, CS);
    SPI.SendData(data);
    SYSTEM.PUT(MCU.FIO0SET, CS);
  END SendData;
 
   
  PROCEDURE SendCommand(data: INTEGER);
  BEGIN
    SYSTEM.PUT(MCU.FIO0CLR, A0);
    SYSTEM.PUT(MCU.FIO0CLR, CS);
    SPI.SendData(data);
    SYSTEM.PUT(MCU.FIO0SET, CS)
  END SendCommand;

   
  PROCEDURE SetColumnAddr(x: INTEGER);
  CONST
    ColumnAddrLo = 000H;
    ColumnAddrHi = 010H;
  BEGIN
    SendCommand(ColumnAddrLo + x MOD 16);
    SendCommand(ColumnAddrHi + x DIV 16)
  END SetColumnAddr;

   
  PROCEDURE SetPageAddr(n: INTEGER);
  CONST
    PageAddrSet  = 0B0H;
  BEGIN
    SendCommand(PageAddrSet + n)
  END SetPageAddr;

 
  PROCEDURE* DrawDot*(colour, x, y: INTEGER);
  BEGIN
    IF (x <= MaxX) & (y <= MaxY) & (x >= 0) & (y >= 0) THEN
      IF colour = Black THEN
        bitMap[x] := bitMap[x] + {y}
      ELSE
        bitMap[x] := bitMap[x] - {y}
      END
    END
  END DrawDot;


  PROCEDURE* DrawVerticalLine*(colour: INTEGER; x, y1, y2: INTEGER);
  VAR
    yBits: SET;
  BEGIN
    IF (x >= 0) & (y1 >= 0) & (y2 >= y1) & (y2 <= MaxY) THEN
      yBits := {y1..y2};
      IF colour = Black THEN
        bitMap[x] := bitMap[x] + yBits
      ELSE
        bitMap[x] := bitMap[x] - yBits
      END
    END
  END DrawVerticalLine;
     
   
  PROCEDURE* FillRectangle*(colour, x1, y1, x2, y2: INTEGER);
  VAR
    x: INTEGER;
    yBits: SET;
  BEGIN
    IF (x >= 0) & (x2 > x1) & (x2 <= MaxX) & (y1 >= 0) & (y2 >= y1) & (y2 <= MaxY) THEN
      yBits := {y1..y2};
      IF colour = Black THEN
        FOR x := x1 TO x2 DO bitMap[x] := bitMap[x] + yBits END
      ELSE
        FOR x := x1 TO x2 DO bitMap[x] := bitMap[x] - yBits END
      END
    END
  END FillRectangle;
   
 
  PROCEDURE* DrawFontChar(fontPattern: FontPattern; col, row: INTEGER);
  VAR
    i, x, adr, fontAdr: INTEGER;
    fontData: BYTE;
  BEGIN
    ASSERT((col >= 0) & (col < Cols) & (row >= 0) & (row < Rows), 100);
    x := (col * FontWidth);
    adr := SYSTEM.ADR(bitMap[x]) + row;
    fontAdr := SYSTEM.ADR(fontPattern);
    FOR i := 0 TO FontWidth - 1  DO
      SYSTEM.GET(fontAdr, fontData, 1);
      SYSTEM.PUT(adr, fontData, 4)
    END
  END DrawFontChar;
 
   
  PROCEDURE DrawChar*(colour: INTEGER; ch: CHAR; col, row: INTEGER);
  VAR
    fontPattern: FontPattern;
  BEGIN
    CharToFontPattern(ch, fontPattern);
    DrawFontChar(fontPattern, col, row)
  END DrawChar;

 
  PROCEDURE Refresh*();
  (* Only write the pixel columns that have changed since the last refresh *)
  VAR
    pageNo, x, adr0, adr: INTEGER;
    data0, data: BYTE;
    col: INTEGER;
  BEGIN
    FOR pageNo := 0 TO Pages - 1 DO
      SetPageAddr(pageNo);
      col := -1;
      adr0 := SYSTEM.ADR(bitMap0) + pageNo;
      adr := SYSTEM.ADR(bitMap) + pageNo;
      FOR x := 0 TO MaxX DO
        SYSTEM.GET(adr0, data0, 4);
        SYSTEM.GET(adr, data, 4);
        IF data # data0 THEN
          IF col # x THEN
            SetColumnAddr(x);
            col := x
          END;
          SendData(data);
          INC(col)
        END
      END
    END;
    bitMap0 := bitMap
  END Refresh;

   
  PROCEDURE* ClearScreen*(colour: INTEGER);
  VAR
    x: INTEGER;
  BEGIN
    IF colour = White THEN
      FOR x := 0 TO MaxX DO
        bitMap[x] := {}
      END
    ELSE
      FOR x := 0 TO MaxX DO
        bitMap[x] := {0..31}
      END
    END
  END ClearScreen;

 
  PROCEDURE* ConfigureSPI1Pins;
  VAR
    s: SET;
  BEGIN
    (* SPI1 *)
    (* Setup    SCK1, SSEL1, MISO1, MOSI1, no SSEL *)
    (* PS0 bits 15:14 12:13  17:16, 19:18 := 10B *)
    SYSTEM.GET(MCU.PINSEL0, s);
    s := s + {15, 17, 19} - {14, 16, 18};
    SYSTEM.PUT(MCU.PINSEL0, s)
  END ConfigureSPI1Pins;
   

  PROCEDURE* ConfigureGPIOPins;
  VAR
    s: SET;
  BEGIN
    (* P0.6, P0.8 are GPIO ports *)
    SYSTEM.GET(MCU.PINSEL0, s);
    s := s - {12, 13, 16, 17};
    SYSTEM.PUT(MCU.PINSEL0, s);

    (* P0.18 is GPIO port *)
    SYSTEM.GET(MCU.PINSEL1, s);
    s := s - {4, 5};
    SYSTEM.PUT(MCU.PINSEL1, s);

    (* P0.6, 0.8 and 0.18 are outputs *)
    SYSTEM.GET(MCU.FIO0DIR, s);
    SYSTEM.PUT(MCU.FIO0DIR, s + A0 + CS + Reset)
  END ConfigureGPIOPins;

   
  PROCEDURE Init*;
  CONST
    nBits = 8;
  BEGIN
    SPI.Init(SPI.SPI1, nBits, ConfigureSPI1Pins);
    ConfigureGPIOPins();
    SYSTEM.PUT(MCU.FIO0CLR, A0);
    SYSTEM.PUT(MCU.FIO0SET, CS);
    SYSTEM.PUT(MCU.FIO0CLR, Reset);
    Timer.uSecDelay(100);
    SYSTEM.PUT(MCU.FIO0SET, Reset);
    Timer.uSecDelay(100);

    SendCommand(0AEH); (* Display off *)
    SendCommand(0A2H); (* Bias voltage *)
 
    SendCommand(0A0H); (* ADC Normal *)
    SendCommand(0C8H); (* COM Scan normal *)
 
    SendCommand(022H); (* Resistor ratio *)
    SendCommand(02FH); (* Power on *)
    SendCommand(040H); (* Display start line 0 *)
 
    SendCommand(081H); (* Set contrast *)
    SendCommand(017H);
 
    SendCommand(0A6H); (* Display normal *)
    ClearScreen(Black);
    bitMap0 := bitMap;
    ClearScreen(White);
    Refresh();
    SendCommand(0AFH); (* DisplayOn *);

  END Init;

END LCDST756R.
User avatar
By AcmeUK
#22354 I love Pascal, you can understand what the other guy was thinking when he wrote the code!
If only Astrobe Oberon could target the ESP8266 mcu

Otherwise it is a dead duck!