0

I often use the Parallel port (D-Sub DB-25 female) of PC to communicate with MCU or a processor card with my unique and self developed software and interface circuit .

Now it looks like I've found a functional bug in the USI hardware of AVR ATtiny45/85 MCU.

I wanted to read the received bytes from the USIBR register instead of USIDR to take advantage of what was being written in data sheet of Atmel (today Microchip's). Here is a quote from 2586Q–AVR–08/2013 data sheet of ATtiny25/45/85(http://www.kynix.com/uploadfiles/pdf8827/ATTINY25-15MT.pdf):

" Instead of reading data from the USI Data Register the USI Buffer Register can be used. This makes controlling the USI less time critical and gives the CPU more time to handle other program tasks. USI flags as set similarly as when reading the USIDR register. The content of the USI Data Register is loaded to the USI Buffer Register when the transfer has been completed. "

I would like to show and proof with my experiment that the USIBR is unusable in the ATtiny45/85 chips, because it loses the MSB bit of the received byte. E.g: if the value of received byte is 3 then 7 is readable from USIBR, if received byte is 4 then the result is 8 from the USIBR.

Generally form is: if the received byte:n then ( (n<<1) + (1&n) )&0xff is readable from USIBR. (It is like a "backward arithmetic shift".)

In my demonstration I used USI in three wire and slave mode and I connected the chip to the Parallel port of my PC via 1kOhm resistors, and I used 5V from a USB plug. And I used the inside calibrated oscillator in default mode 8MHz with CKDIV8 fuse so speed was 1usec/1instruction (if it is one cycle instruction.) The accurate pin assignment between PC and ATtiny45 ("the interface circuit") has been seen top of my C program in a comment.

This C program in the PC acts as a three wire master under Linux. It uses very slow clock: 1ms for half period and wait 10ms between bytes. This master sends the series of one byte integers to the slave. The slave assembly program in the chip only echoes back to the received bytes and the master gets this echo and print them on the screen. Of course the received byte was coupled with the previously sent byte in the printed string.

The master and slave program demonstrates the successful operation when I use the "IN R16,USIDR" line in slave. But if I change this one line to "IN R16,USIBR" then the echo from the slave to the master is satisfies the above strange form that means the MSB had been lost in USIBR. I think we can conclude that USIBR is unusable.

Please look at my sources. I tried to keep the source code as short as possible both in C and assembly. And I put the two Intel HEX outputs also for the clarity. This also shows that only one instruction word was different in the slave program (B10F is godd case but B300 isn't working (data part of Intell HEX is little endian)).

I'm going to send a bug report to Microchip from this, because this problem isn't in the Errata of the last 2586Q–AVR–08/2013 data sheet.

I wonder if anyone can confirm the problem of USIBR or refute it?

Here is the Echo prg (slave on three wire):

; This is a slave on 3 wire
; and echo back the received bytes.

        .EQU   OK = 1       ;If OK=1 then "in a,usidr",
                            ;If OK=0 then "in a,usibr" will be compiled 
        .nolist
        .include   "/usr/share/avra/tn45def.inc"
        .list
;       .device ATtiny45
        .def    a =  r16
        .cseg
        .org    0x0000       ;IT vector not used
        bclr    sreg_i       ;disable IT forever
        sbi     ddrb, ddb1   ;DB1(MISO) is output, all other is input

        ldi     a, 0x18     ;SIE:0 OIE:0 WM1:0 WM0:1 CS1:1 CS0:0 CLK:0 TC:0
        out     usicr, a    ;set USI to three wire as slave

cyc0:   sbi     usisr, usioif   ;clr USIOIF
cyc1:   sbis    usisr, usioif   ;skip if received a byte
        rjmp    cyc1            ;wait for a byte from master

        .IF     OK
        in      a, usidr        ;get the byte from usidr
        .ELSE
        in      a, usibr        ;get the byte from usibr it's DON'T working
        .ENDIF

        out     usidr, a        ;echo back the received byte
        rjmp    cyc0            ; do forever

;  If .EQU OK = 1 then Intel HEX output this: and USIDR is used successful
;  :020000020000FC
;  :10000000F894B99A08E10DB9769A769BFECF0FB1B4
;  :040010000FB9FACF5B
;  :00000001FF

;  If .EQU OK = 0 then Intel HEX output this: but USIBR is used with FAULT
;  :020000020000FC
;  :10000000F894B99A08E10DB9769A769BFECF00B3C1
;  :040010000FB9FACF5B
;  :00000001FF

If .EQU OK = 1 in the above source, then the part of output screen of master.c

Received: 00 == Previously sent: 00
Received: 01 == Previously sent: 01
Received: 02 == Previously sent: 02
Received: 03 == Previously sent: 03
Received: 04 == Previously sent: 04

If .EQU OK = 0 in the above source, then the part of output screen of master.c

Received: 00 == Previously sent: 00
Received: 03 != Previously sent: 01
Received: 04 != Previously sent: 02
Received: 07 != Previously sent: 03
....
Received: 00 != Previously sent: 80
Received: 03 != Previously sent: 81
Received: 04 != Previously sent: 82
Received: 07 != Previously sent: 83
....
Received: e8 != Previously sent: f4
Received: eb != Previously sent: f5
Received: ec != Previously sent: f6
Received: ef != Previously sent: f7

Finally, here is my C program acts as a three wire master under Linux via Parallel por of PC.

/*
Test of three wire between PC & ATtiny45

  This prg acts as master of 3wire.
  It sends series of byte 0, 1, 2 ...
  And it prints the received and
  that was sent in before.

  Pin connection between PC and ATtiny45:
  PC Parallel port
  D-Sub DB-25 Female              ATtiny45
  PORT.D0 (pin2) --> 1kOhm --> PB0 (pin5) (MOSI)
  PORT.D1 (pin3) --> 1kOhm --> PB2 (pin7) (SCK)
  (PORT+1).D6 (pin10) <-- 1kOhm <-- PB1 (pin6) MISO
  PORT.D2 (pin4) --> 1kOhm --> PB5 (pin1) ~RST
  GND (pin25) -------------------GND (pin4)

  Power is 5V (from a USB plug).
  And inside calibrated oscillator was used
  in default mode (8MHz and div8) so
  1usec/one instruction (if it is one cycle).

gcc -O0 three_wire_test.c -o three_wire_test -lrt

If you want to use this prg by a normal user:
chown root:laci three_wire_test && chmod +s three_wire_test
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/io.h>
#include <sys/time.h>

#define PORT    0x378      //Others: 0x278 0x3bc

//very slow clock
#define W100    100000     // 100ms reset
#define W1      1000       // 1ms half ck
#define W10     10000      // 10ms after a byte transfer

void wait_u(int);
int snd_rec_byte(int);

int main(){
int i, snt, snt_1, rec;

// Get permission for direct I/O under Linux:
if(ioperm(PORT,2,1)){
    printf("Couldn't open parallel port 0x%x\n", PORT); exit(1);}

//One reset pulse 
outb(4, PORT); wait_u(W100); outb(0, PORT); wait_u(W100);
outb(4, PORT); wait_u(W100); // waiting for startup of chip

// send, receive and print bytes
for(snt_1=-1, snt=0; ;snt_1=snt, snt++, snt&=0xff){
    rec=snd_rec_byte(snt);
    if(snt_1!=-1)printf("Received: %02x %s Previously sent: %02x\n",\
                         rec, rec==snt_1?"==":"!=", snt_1);}
} //End of main

int snd_rec_byte(int s){                 // One byte send & receive
int r, i;
for(r=i=0 ; i<8; i++, s<<=1){            // 8 bit shifting
    outb(s&0x80?5:4, PORT); wait_u(W1);  // hold SCK=L,MOSI=MSBofs; wait 1ms
    outb(s&0x80?7:6, PORT); wait_u(W1);  // rise SCK=H,hold MOSI; wait 1ms
    r<<=1; if(inb(PORT+1)&0x40)r|=1;     // MISO shift into r,outputs unchanged
    outb(s&0x80?5:4, PORT); wait_u(W1);} // fall SCK=L,HOLD MOSI, wait 1ms
wait_u(W10); return(r);
} // End of snd_rec_byte

void wait_u(int c){                      // Delay minimum c usec
struct timeval req, req2;
gettimeofday (&req, NULL);
for(;;){
    gettimeofday (&req2, NULL);
    if((req2.tv_sec-req.tv_sec)*1000000+req2.tv_usec-req.tv_usec >= c)return;}
} // End of wait_u

Edited by happygeek: moved

2
Contributors
1
Reply
15
Views
3 Months
Discussion Span
Last Post by rproffitt
0

I did not spend hours looking into this. But here's my thought. The shift left using << could lose the MSB since it's only a byte. Maybe use a 16 bit variable so the MSB is not tossed.

Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.