Recent

Author Topic: UnoLib - library in Pascal for Arduino Uno and ATMega328p  (Read 16425 times)

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #15 on: March 01, 2025, 02:16:57 pm »
We prepared new translations of PulseIn routines. I placed them in dedicated pulse.pas unit (attached). This unit includes:
- pulseIn (based partially on assembler routine; returns time in microseconds)
- pulseInLong (returns time in microseconds)
- pulseInMilli (written by @Dzandaa, returns time in milliseconds)
Also test program using HC-SR04 sensor is attached.

I have some doubts on the assembler routine pulseInSimplAsm. It is simply function pulseInSimpl compiled and disassembled via avr-objdump to find number of clock cycles in main iteration (20 as I estimated). My doubts are about the registers, I am not sure if these particular registers can be used without problems and without negatively affecting the rest of the program...

ccrause

  • Hero Member
  • *****
  • Posts: 1083
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #16 on: March 01, 2025, 04:34:33 pm »
We prepared new translations of PulseIn routines. I placed them in dedicated pulse.pas unit (attached). This unit includes:
- pulseIn (based partially on assembler routine; returns time in microseconds)
- pulseInLong (returns time in microseconds)
- pulseInMilli (written by @Dzandaa, returns time in milliseconds)
Also test program using HC-SR04 sensor is attached.
After a quick look I think that the local variable width in function pulseInSimpl is unnecessary and can be replaced by Result.  This wouldn't make the body of the function much smaller, but would reduce stack use, since width is stored on the stack.

Quote
I have some doubts on the assembler routine pulseInSimplAsm. It is simply function pulseInSimpl compiled and disassembled via avr-objdump to find number of clock cycles in main iteration (20 as I estimated). My doubts are about the registers, I am not sure if these particular registers can be used without problems and without negatively affecting the rest of the program...
The obvious consideration is that the compiler generated the code for the Pascal function, so it should be safe to call in the context of the current program.  FPC follows the avr-gcc ABI and the usage of registers (which registers can be left modified and which registers should be restored when a routine returns) is described here.

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #17 on: March 01, 2025, 05:23:26 pm »
@ccrause, thanks for your comments :)

After a quick look I think that the local variable width in function pulseInSimpl is unnecessary and can be replaced by Result.  This wouldn't make the body of the function much smaller, but would reduce stack use, since width is stored on the stack.

Of course you are right. But this variable appears in original C code, so I used it.

The obvious consideration is that the compiler generated the code for the Pascal function, so it should be safe to call in the context of the current program.  FPC follows the avr-gcc ABI and the usage of registers (which registers can be left modified and which registers should be restored when a routine returns) is described here.

Thanks for the link. So according to ABI rules the function should work even after changing the compiler optimization level, do I understand correctly?

I don't know if the arduino compiler uses ABI rules (but uses code generated by avr-gcc). If yes - perhaps it's possible to use the original assembler code from the arduino sources (please correct me if I'm wrong):

Code: Text  [Select][+][-]
  1.  
  2. /*
  3.  * The following routine was generated by avr-gcc 4.8.3 with the following parameters
  4.  * -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
  5.  * on the original C function
  6.  *
  7.  * unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
  8.  * {
  9.  *     unsigned long width = 0;
  10.  *     // wait for any previous pulse to end
  11.  *     while ((*port & bit) == stateMask)
  12.  *         if (--maxloops == 0)
  13.  *             return 0;
  14.  *
  15.  *     // wait for the pulse to start
  16.  *     while ((*port & bit) != stateMask)
  17.  *         if (--maxloops == 0)
  18.  *             return 0;
  19.  *
  20.  *     // wait for the pulse to stop
  21.  *     while ((*port & bit) == stateMask) {
  22.  *         if (++width == maxloops)
  23.  *             return 0;
  24.  *     }
  25.  *     return width;
  26.  * }
  27.  *
  28.  * some compiler outputs were removed but the rest of the code is untouched
  29.  */
  30.  
  31. #include <avr/io.h>
  32.  
  33. .section .text
  34.  
  35. .global countPulseASM
  36.  
  37. countPulseASM:
  38.  
  39. .LM0:
  40. .LFBB1:
  41.     push r12   ;   ;  130 pushqi1/1 [length = 1]
  42.     push r13   ;   ;  131 pushqi1/1 [length = 1]
  43.     push r14   ;   ;  132 pushqi1/1 [length = 1]
  44.     push r15   ;   ;  133 pushqi1/1 [length = 1]
  45.     push r16   ;   ;  134 pushqi1/1 [length = 1]
  46.     push r17   ;   ;  135 pushqi1/1 [length = 1]
  47. /* prologue: function */
  48. /* frame size = 0 */
  49. /* stack size = 6 */
  50. .L__stack_usage = 6
  51.     mov r30,r24  ;  port, port   ;  2 *movhi/1  [length = 2]
  52.     mov r31,r25  ;  port, port
  53. /*     unsigned long width = 0;
  54. ***     // wait for any previous pulse to end
  55. ***     while ((*port & bit) == stateMask)
  56. */
  57. .LM1:
  58.     rjmp .L2   ;   ;  181 jump  [length = 1]
  59. .L4:
  60. /*         if (--maxloops == 0) */
  61. .LM2:
  62.     subi r16,1   ;  maxloops,  ;  17  addsi3/2  [length = 4]
  63.     sbc r17, r1   ;  maxloops
  64.     sbc r18, r1   ;  maxloops
  65.     sbc r19, r1   ;  maxloops
  66.     breq .L13  ; ,   ;  19  branch  [length = 1]
  67. .L2:
  68. /*         if (--maxloops == 0) */
  69. .LM3:
  70.     ld r25,Z   ;  D.1554, *port_7(D)   ;  22  movqi_insn/4  [length = 1]
  71.     and r25,r22  ;  D.1554, bit  ;  24  andqi3/1  [length = 1]
  72.     cp r25,r20   ;  D.1554, stateMask  ;  25  *cmpqi/2  [length = 1]
  73.     breq .L4   ; ,   ;  26  branch  [length = 1]
  74.     rjmp .L6   ;   ;  184 jump  [length = 1]
  75. .L7:
  76. /*             return 0;
  77. ***
  78. ***     // wait for the pulse to start
  79. ***     while ((*port & bit) != stateMask)
  80. ***         if (--maxloops == 0)
  81. */
  82. .LM4:
  83.     subi r16,1   ;  maxloops,  ;  31  addsi3/2  [length = 4]
  84.     sbc r17, r1   ;  maxloops
  85.     sbc r18, r1   ;  maxloops
  86.     sbc r19, r1   ;  maxloops
  87.     breq .L13  ; ,   ;  33  branch  [length = 1]
  88. .L6:
  89. /*         if (--maxloops == 0) */
  90. .LM5:
  91.     ld r25,Z   ;  D.1554, *port_7(D)   ;  41  movqi_insn/4  [length = 1]
  92.     and r25,r22  ;  D.1554, bit  ;  43  andqi3/1  [length = 1]
  93.     cpse r25,r20   ;  D.1554, stateMask  ;  44  enable_interrupt-3  [length = 1]
  94.     rjmp .L7   ;
  95.     mov r12, r1   ;  width  ;  7 *movsi/2  [length = 4]
  96.     mov r13, r1   ;  width
  97.     mov r14, r1   ;  width
  98.     mov r15, r1   ;  width
  99.     rjmp .L9   ;   ;  186 jump  [length = 1]
  100. .L10:
  101. /*             return 0;
  102. ***
  103. ***     // wait for the pulse to stop
  104. ***     while ((*port & bit) == stateMask) {
  105. ***         if (++width == maxloops)
  106. */
  107. .LM6:
  108.     ldi r24,-1   ; ,   ;  50  addsi3/3  [length = 5]
  109.     sub r12,r24  ;  width,
  110.     sbc r13,r24  ;  width,
  111.     sbc r14,r24  ;  width,
  112.     sbc r15,r24  ;  width,
  113.     cp r16,r12   ;  maxloops, width  ;  51  *cmpsi/2  [length = 4]
  114.     cpc r17,r13  ;  maxloops, width
  115.     cpc r18,r14  ;  maxloops, width
  116.     cpc r19,r15  ;  maxloops, width
  117.     breq .L13  ; ,   ;  52  branch  [length = 1]
  118. .L9:
  119. /*         if (++width == maxloops) */
  120. .LM7:
  121.     ld r24,Z   ;  D.1554, *port_7(D)   ;  60  movqi_insn/4  [length = 1]
  122.     and r24,r22  ;  D.1554, bit  ;  62  andqi3/1  [length = 1]
  123.     cp r24,r20   ;  D.1554, stateMask  ;  63  *cmpqi/2  [length = 1]
  124.     breq .L10  ; ,   ;  64  branch  [length = 1]
  125. /*             return 0;
  126. ***     }
  127. ***     return width;
  128. */
  129. .LM8:
  130.     mov r22,r12  ;  D.1553, width  ;  108 movqi_insn/1  [length = 1]
  131.     mov r23,r13  ;  D.1553, width  ;  109 movqi_insn/1  [length = 1]
  132.     mov r24,r14  ;  D.1553, width  ;  110 movqi_insn/1  [length = 1]
  133.     mov r25,r15  ;  D.1553, width  ;  111 movqi_insn/1  [length = 1]
  134. /* epilogue start */
  135. .LM9:
  136.     pop r17  ;   ;  171 popqi [length = 1]
  137.     pop r16  ;   ;  172 popqi [length = 1]
  138.     pop r15  ;   ;  173 popqi [length = 1]
  139.     pop r14  ;   ;  174 popqi [length = 1]
  140.     pop r13  ;   ;  175 popqi [length = 1]
  141.     pop r12  ;   ;  176 popqi [length = 1]
  142.     ret  ;  177 return_from_epilogue  [length = 1]
  143. .L13:
  144. .LM10:
  145.     ldi r22,0  ;  D.1553   ;  120 movqi_insn/1  [length = 1]
  146.     ldi r23,0  ;  D.1553   ;  121 movqi_insn/1  [length = 1]
  147.     ldi r24,0  ;  D.1553   ;  122 movqi_insn/1  [length = 1]
  148.     ldi r25,0  ;  D.1553   ;  123 movqi_insn/1  [length = 1]
  149. /* epilogue start */
  150. .LM11:
  151.     pop r17  ;   ;  138 popqi [length = 1]
  152.     pop r16  ;   ;  139 popqi [length = 1]
  153.     pop r15  ;   ;  140 popqi [length = 1]
  154.     pop r14  ;   ;  141 popqi [length = 1]
  155.     pop r13  ;   ;  142 popqi [length = 1]
  156.     pop r12  ;   ;  143 popqi [length = 1]
  157.     ret  ;  144 return_from_epilogue  [length = 1]
  158.  
  159.  

Of course it is not necessary, but for fun it can be done :)

Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

Code: Pascal  [Select][+][-]
  1.  
  2. function pulseInSimplAsmOrg(PortPtr: PUint8; bit, stateMask: Uint8; maxloops: UInt32): UInt32; assembler; nostackframe;
  3. label
  4. //  LFBB1,
  5.   L2,
  6.   L4,
  7.   L13,
  8.   L6,
  9.   L7,
  10.   //LM0,
  11.   //LM1,
  12.   //LM2,
  13.   //LM3,
  14.   //LM4,
  15.   //LM5,
  16.   L9,
  17.   L10;
  18.   //LM6,
  19.   //LM7,
  20.   //LM8,
  21.   //LM9,
  22.   //LM10,
  23.   //LM11;
  24. asm
  25. //LM0:
  26. //LFBB1:
  27.     push r12   //   //  130 pushqi1/1 [length = 1]
  28.     push r13   //   //  131 pushqi1/1 [length = 1]
  29.     push r14   //   //  132 pushqi1/1 [length = 1]
  30.     push r15   //   //  133 pushqi1/1 [length = 1]
  31.     push r16   //   //  134 pushqi1/1 [length = 1]
  32.     push r17   //   //  135 pushqi1/1 [length = 1]
  33. // prologue: function
  34. // frame size = 0
  35. // stack size = 6
  36. //.L__stack_usage = 6
  37.     mov r30,r24  //  port, port   //  2 *movhi/1  [length = 2]
  38.     mov r31,r25  //  port, port
  39. //     unsigned long width = 0//
  40. //     // wait for any previous pulse to end
  41. //     while ((*port & bit) == stateMask)
  42.  
  43. //LM1:
  44.     rjmp L2   //   //  181 jump  [length = 1]
  45. L4:
  46. //         if (--maxloops == 0)
  47. //LM2:
  48.     subi r16,1   //  maxloops,  //  17  addsi3/2  [length = 4]
  49.     sbc r17, r1   //  maxloops
  50.     sbc r18, r1   //  maxloops
  51.     sbc r19, r1   //  maxloops
  52.     breq L13  // ,   //  19  branch  [length = 1]
  53. L2:
  54. //         if (--maxloops == 0)
  55. //LM3:
  56.     ld r25,Z   //  D.1554, *port_7(D)   //  22  movqi_insn/4  [length = 1]
  57.     and r25,r22  //  D.1554, bit  //  24  andqi3/1  [length = 1]
  58.     cp r25,r20   //  D.1554, stateMask  //  25  *cmpqi/2  [length = 1]
  59.     breq L4   // ,   //  26  branch  [length = 1]
  60.     rjmp L6   //   //  184 jump  [length = 1]
  61. L7:
  62. //             return 0
  63. //
  64. //    // wait for the pulse to start
  65. //     while ((*port & bit) != stateMask)
  66. //         if (--maxloops == 0)
  67.  
  68. //LM4:
  69.     subi r16,1   //  maxloops,  //  31  addsi3/2  [length = 4]
  70.     sbc r17, r1   //  maxloops
  71.     sbc r18, r1   //  maxloops
  72.     sbc r19, r1   //  maxloops
  73.     breq L13  // ,   //  33  branch  [length = 1]  <-- @ackarwow here error "conditional branch destination is out of range"
  74. L6:
  75. //         if (--maxloops == 0)
  76. //LM5:
  77.     ld r25,Z   //  D.1554, *port_7(D)   //  41  movqi_insn/4  [length = 1]
  78.     and r25,r22  //  D.1554, bit  //  43  andqi3/1  [length = 1]
  79.     cpse r25,r20   //  D.1554, stateMask  //  44  enable_interrupt-3  [length = 1]
  80.     rjmp L7   //
  81.     mov r12, r1   //  width  //  7 *movsi/2  [length = 4]
  82.     mov r13, r1   //  width
  83.     mov r14, r1   //  width
  84.     mov r15, r1   //  width
  85.     rjmp L9   //   //  186 jump  [length = 1]
  86. L10:
  87. //             return 0//
  88. //
  89. //     // wait for the pulse to stop
  90. //     while ((*port & bit) == stateMask) {
  91. //         if (++width == maxloops)
  92. //
  93. //LM6:
  94.     ldi r24,-1   // ,   //  50  addsi3/3  [length = 5]
  95.     sub r12,r24  //  width,
  96.     sbc r13,r24  //  width,
  97.     sbc r14,r24  //  width,
  98.     sbc r15,r24  //  width,
  99.     cp r16,r12   //  maxloops, width  //  51  *cmpsi/2  [length = 4]
  100.     cpc r17,r13  //  maxloops, width
  101.     cpc r18,r14  //  maxloops, width
  102.     cpc r19,r15  //  maxloops, width
  103.     breq L13  // ,   //  52  branch  [length = 1]  <-- @ackarwow here error "conditional branch destination is out of range"
  104. L9:
  105. //         if (++width == maxloops)
  106. //LM7:
  107.     ld r24,Z   //  D.1554, *port_7(D)   //  60  movqi_insn/4  [length = 1]
  108.     and r24,r22  //  D.1554, bit  //  62  andqi3/1  [length = 1]
  109.     cp r24,r20   //  D.1554, stateMask  //  63  *cmpqi/2  [length = 1]
  110.     breq L10  // ,   //  64  branch  [length = 1]
  111. //             return 0//
  112. //
  113. //     return width//
  114. //
  115. //LM8:
  116.     mov r22,r12  //  D.1553, width  //  108 movqi_insn/1  [length = 1]
  117.     mov r23,r13  //  D.1553, width  //  109 movqi_insn/1  [length = 1]
  118.     mov r24,r14  //  D.1553, width  //  110 movqi_insn/1  [length = 1]
  119.     mov r25,r15  //  D.1553, width  //  111 movqi_insn/1  [length = 1]
  120. // epilogue start
  121. //LM9:
  122.     pop r17  //   //  171 popqi [length = 1]
  123.     pop r16  //   //  172 popqi [length = 1]
  124.     pop r15  //   //  173 popqi [length = 1]
  125.     pop r14  //   //  174 popqi [length = 1]
  126.     pop r13  //   //  175 popqi [length = 1]
  127.     pop r12  //   //  176 popqi [length = 1]
  128. //    ret  //  177 return_from_epilogue  [length = 1]
  129. L13:
  130. //LM10:
  131.     ldi r22,0  //  D.1553   //  120 movqi_insn/1  [length = 1]
  132.     ldi r23,0  //  D.1553   //  121 movqi_insn/1  [length = 1]
  133.     ldi r24,0  //  D.1553   //  122 movqi_insn/1  [length = 1]
  134.     ldi r25,0  //  D.1553   //  123 movqi_insn/1  [length = 1]
  135. // epilogue start
  136. //LM11:
  137.     pop r17  //   //  138 popqi [length = 1]
  138.     pop r16  //   //  139 popqi [length = 1]
  139.     pop r15  //   //  140 popqi [length = 1]
  140.     pop r14  //   //  141 popqi [length = 1]
  141.     pop r13  //   //  142 popqi [length = 1]
  142.     pop r12  //   //  143 popqi [length = 1]
  143. //    ret  //  144 return_from_epilogue  [length = 1]
  144. end;
  145.  
  146.  
« Last Edit: March 01, 2025, 09:04:50 pm by ackarwow »

ccrause

  • Hero Member
  • *****
  • Posts: 1083
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #18 on: March 02, 2025, 06:12:24 am »
The obvious consideration is that the compiler generated the code for the Pascal function, so it should be safe to call in the context of the current program.  FPC follows the avr-gcc ABI and the usage of registers (which registers can be left modified and which registers should be restored when a routine returns) is described here.

Thanks for the link. So according to ABI rules the function should work even after changing the compiler optimization level, do I understand correctly?
Yes

Quote
I don't know if the arduino compiler uses ABI rules (but uses code generated by avr-gcc). If yes - perhaps it's possible to use the original assembler code from the arduino sources (please correct me if I'm wrong):
There is no Arduino compiler, it uses existing tool chains.  For AVR controllers it uses avr-gcc as compiler, so yes in principle the assembler code should conform to the same ABI and can be re-used in Pascal.

ccrause

  • Hero Member
  • *****
  • Posts: 1083
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #19 on: March 02, 2025, 07:56:37 am »
Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

I suspect this is due to the compiler calculating offsets based on code size in bytes, but branch offsets should be word based offsets.  Basically a compiler bug, I am working on a fix.

ccrause

  • Hero Member
  • *****
  • Posts: 1083
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #20 on: March 02, 2025, 02:04:25 pm »
Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

I suspect this is due to the compiler calculating offsets based on code size in bytes, but branch offsets should be word based offsets.  Basically a compiler bug, I am working on a fix.

Refer to #41174 for details.

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #21 on: March 02, 2025, 04:29:30 pm »
Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

I suspect this is due to the compiler calculating offsets based on code size in bytes, but branch offsets should be word based offsets.  Basically a compiler bug, I am working on a fix.

Refer to #41174 for details.

@ccrause, thank you :) I updated my local FPC sources (aasmcpu.pas with your changes) and now the function compiles without errors!

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #22 on: March 29, 2025, 04:54:34 pm »
I translated tone unit from Arduino library. Original code is slightly complicated because of many variants of mcus and timers. I simplified my code using only the one for ATMega328p. Also a test program using buzzer to play simple melody is attached.

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #23 on: July 28, 2025, 09:18:08 pm »
UnoLib 1.0 has been published on SourceForge (https://sourceforge.net/projects/unolib/)
Changes:
 - added square wave tone routines and example (ToneNoInt by @Myel)
 - added pulsein routines and example (pulseInMilli by @Dzandaa)
 - added i2c module and examples of its use (by @Dzandaa)
 - added sine table generation for conv2hexa (by @Dzandaa)

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #24 on: August 04, 2025, 05:43:26 pm »
I updated Unolib GitHub repository (https://github.com/ackarwow/unolib). All changes are related to USB support in Leonardo. Many thanks to @ccrause for pointing out the problems and some bugs in code.

I am still having trouble programming Leonardo. After uploading the code, I can't reset the board port and reprogram the board. I don't know what's causing it. Maybe the bootloader is not triggered correctly? Does anyone have any ideas?

@Dzandaa, your RS232 terminal program is quite good for testing Leonardo communication via USB :)

ccrause

  • Hero Member
  • *****
  • Posts: 1083
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #25 on: August 04, 2025, 07:09:39 pm »
I updated Unolib GitHub repository (https://github.com/ackarwow/unolib). All changes are related to USB support in Leonardo. Many thanks to @ccrause for pointing out the problems and some bugs in code.
Thank you for updating the file, I can confirm that the USB-CDC functionality now works for both sending and receiving data (only single characters tested, but it looks promising).

Quote
I am still having trouble programming Leonardo. After uploading the code, I can't reset the board port and reprogram the board. I don't know what's causing it. Maybe the bootloader is not triggered correctly? Does anyone have any ideas?

@Dzandaa, your RS232 terminal program is quite good for testing Leonardo communication via USB :)
I also noticed this, before the Arduino IDE could enter bootloader automatically (if I remember correctly, I haven't tested it too much), now it doesn't. At least the booloader can be activated by resetting the board, so it is still functional, just not as convenient.

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #26 on: August 06, 2025, 08:52:48 pm »
I have updated UnoLib sources (https://github.com/ackarwow/unolib) based on new bug findings by @ccrause. As he commented in issue #2 (https://github.com/ackarwow/unolib/issues/2) he can upload code for Leonardo, and the board triggers bootloader correctly. My tests did not confirm this, and after enabling watchdog I had to add extra infinite loop. This extra code I enclosed in {IFDEF AVRPASCAL} block.

My tests also show differences in how AVRDude (8.0) works. On Windows everything is ok: 

Code: Text  [Select][+][-]
  1. Forcing reset using 1200bps open/close on port com3...
  2. Waiting for port...
  3. Found port COM4.
  4. Reading 4140 bytes for flash from input file ccrause_test_leonardo.hex
  5. Writing 4140 bytes to flash
  6. Writing | ################################################## | 100% 0.58s
  7. Reading | ################################################## | 100% 0.11s
  8. 4140 bytes of flash verified
  9.  
  10. Avrdude done.  Thank you.
  11. Waiting for port...
  12. Found port COM3.
  13.  

But on Linux writing to flash failed:

Code: Text  [Select][+][-]
  1. Forcing reset using 1200bps open/close on port /dev/ttyACM0...
  2. Waiting for port...
  3. Found port /dev/ttyACM0.
  4. Reading 4140 bytes for flash from input file ccrause_test_leonardo.hex
  5. Writing 4140 bytes to flash
  6. Writing | -------------------------------------------------- | 0% 8.43s
  7. Error: butterfly_recv(pgm, (char *) &m->buf[addr], blocksize) failed
  8. Error: protocol error for command: set addr
  9. Error: butterfly_recv(pgm, buf, sizeof(buf)) failed
  10. Error: butterfly_recv(pgm, &c, 1) failed
  11. Reading | ################################################## | 100% 0.47s
  12. 4140 bytes of flash verified
  13.  
  14. Avrdude done.  Thank you.
  15. Waiting for port...
  16. Found port /dev/ttyACM0.
  17.  

Moreover, IDE before uploading should reset board using 1200 baud connection. Both AVRPascal and Arduino IDE do this. Although I am not absolutely sure I suppose the differences depend on AVRDude version (OS/number). Such differences were already reported in the AVRPascal thread:

https://forum.lazarus.freepascal.org/index.php/topic,68795.msg537810.html#msg537810
https://forum.lazarus.freepascal.org/index.php/topic,68795.msg537811.html#msg537811

So maybe the current UnoLib code is good enough?


« Last Edit: August 06, 2025, 09:25:40 pm by ackarwow »

ccrause

  • Hero Member
  • *****
  • Posts: 1083
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #27 on: August 08, 2025, 08:06:43 am »
But on Linux writing to flash failed:

Code: Text  [Select][+][-]
  1. Forcing reset using 1200bps open/close on port /dev/ttyACM0...
  2. Waiting for port...
  3. Found port /dev/ttyACM0.
  4. Reading 4140 bytes for flash from input file ccrause_test_leonardo.hex
  5. Writing 4140 bytes to flash
  6. Writing | -------------------------------------------------- | 0% 8.43s
  7. Error: butterfly_recv(pgm, (char *) &m->buf[addr], blocksize) failed
  8. Error: protocol error for command: set addr
  9. Error: butterfly_recv(pgm, buf, sizeof(buf)) failed
  10. Error: butterfly_recv(pgm, &c, 1) failed
  11. Reading | ################################################## | 100% 0.47s
  12. 4140 bytes of flash verified
  13.  
  14. Avrdude done.  Thank you.
  15. Waiting for port...
  16. Found port /dev/ttyACM0.
  17.  

Did you pass the -rr parameter to avrdude?  Below I can directly program a Leonardo using latest avrdude 8.1:
Code: Bash  [Select][+][-]
  1. $ avrdude -c avr109 -p m32u4 -P /dev/ttyACM0 -v -U flash:w:usbserial.hex:i -rr
  2. Avrdude version 8.1-20250625 (b4403627)
  3. Copyright see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
  4.  
  5. System wide configuration file is /usr/local/etc/avrdude.conf
  6. User configuration file ~/.avrduderc does not exist
  7.  
  8. Touching serial port /dev/ttyACM0 at 1200 baud
  9. Waiting for new port... 850 ms: using new port /dev/ttyACM0
  10. Using port            : /dev/ttyACM0
  11. Using programmer      : avr109
  12. AVR part              : ATmega32U4
  13. Programming modes     : SPM, ISP, HVPP, JTAG
  14. Programmer type       : butterfly
  15. Description           : Atmel bootloader (AVR109, AVR911)
  16. connecting to programmer: .
  17. Programmer id    = CATERIN; type = S
  18. Software version = 1.0; no hardware version given
  19. programmer supports auto addr increment
  20. programmer supports buffered memory access with buffersize=128 bytes
  21. Devcode selected: 0x44
  22.  
  23. AVR device initialized and ready to accept instructions
  24. Device signature = 1E 95 87 (ATmega32U4)
  25. Auto-erasing chip as flash memory needs programming (-U flash:w:...)
  26. specify the -D option to disable this feature
  27. Erased chip
  28. Reading 5318 bytes for flash from input file usbserial.hex
  29. in 1 section [0, 0x14c5]: 42 pages and 58 pad bytes
  30. Writing 5318 bytes to flash
  31. Writing | ################################################## | 100% 0.43 s
  32. Reading | ################################################## | 100% 0.05 s
  33. 5318 bytes of flash verified
  34.  
  35. Avrdude done.  Thank you.

Quote
So maybe the current UnoLib code is good enough?
Based on my testing I would say yes!

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #28 on: August 16, 2025, 09:37:26 am »
@ccrause, thanks for the suggestions, and sorry for the late reply, I only had time for some detailed tests yesterday. Both the UnoLib code for Leonardo and the AVR Pascal upload works fine. Also AVRDude is OK and not require additional parameters. The reason for the failure is insufficient user permissions for the serial port on my Linux. I fixed it by adding the 90-extraacl.rules file to /etc/udev/rules.d:

Code: Text  [Select][+][-]
  1. KERNEL=="ttyUSB[0-9]*", TAG+="udev-acl", TAG+="uaccess", OWNER="<user name>"
  2. KERNEL=="ttyACM[0-9]*", TAG+="udev-acl", TAG+="uaccess", OWNER="<user name>"
  3.  

Now uploading to Leonardo works without any problems:

Code: Text  [Select][+][-]
  1. Forcing reset using 1200bps open/close on port /dev/ttyACM0...
  2. Waiting for port...
  3. Found port /dev/ttyACM0.
  4. Reading 4140 bytes for flash from input file ccrause_test_leonardo.hex
  5. Writing 4140 bytes to flash
  6. Writing | ################################################## | 100% 0.58s
  7. Reading | ################################################## | 100% 0.48s
  8. 4140 bytes of flash verified
  9.  
  10. Avrdude done.  Thank you.
  11. Waiting for port...
  12. Found port /dev/ttyACM0.
  13.  

Sorry for the confusion.

Edit: This solution sometimes helps, sometimes doesn't... I decided to check what could be blocking the serial port:
Code: Text  [Select][+][-]
  1. sudo lsof /dev/ttyACM0
  2.  
It turned out that during programming, ModemManager was listed together with AVRDude. Since my Linux machine only serves as a development environment, I uninstalled ModemManager. A similar issue is described here: https://www.udoo.org/docs-bolt/Arduino_Leonardo_compatible(ATmega32U4)/Getting_Started_with_Arduino_Leonardo.html. I hope this is the end of my problems with Arduino Leonardo on Linux.

« Last Edit: August 17, 2025, 01:34:01 am by ackarwow »

ackarwow

  • Full Member
  • ***
  • Posts: 165
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #29 on: August 16, 2025, 11:37:01 am »
The new version of UnoLib (1.1) is now available on SourceForge (https://sourceforge.net/projects/unolib/). The changes are mainly code fixes for the Arduino Leonardo. Many thanks to @ccrause for his suggestions and help.

 

TinyPortal © 2005-2018