MCP4251 with Atmega without SPI mode

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear All ,

Any one is having any idea of how to control MCP4251 digital POT using Atmega with normal port pin, I am doing as per Microchip manual but fails repeatedly.

i am using SDI, SCL, CS/ signals.

 

My code is similar like this : ( Not exactly)

//------------------------------------------------------------

// Start write
        PORTB |= SCK; // clock goes high
        _delay_us(20);
        PORTB &= ~CS; // cs goes low to start write
        _delay_us(20);
        unsigned short mask = 0b1000000000000000; // MSB
        for (int n=0; n<16; n++) // 16 bits, 2 bytes: command byte plus data byte
        {
            PORTB &= ~SCK; // clock goes low
            _delay_us(20);
            
            // Prepare pin
            if ((output & mask) > 0) // Bit is on
                PORTB |= SI // SI on
            else
                PORTB &= ~SI;
            
            _delay_us(20);

            PORTB |= SCK; // clock goes high
            _delay_us(20); // stays high for a bit while bit is read by DPOT
            
            mask = mask>>1; // next bit down
        }
        PORTB |= CS; // CS high again to end write

        _delay_ms(1); // 

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not sure why you have those huge delays, the SCLK low and high times of the MCP4251 worst case is 500nS (at lower voltages).

What are the definitions for CS, SDI and SCK?

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Since you have decided not to show us the real code, I can only guess!  

Every where you have a statement like this:  PORTB |= CS;   change it to be PORTB |= (1<<CS), or change PORTB &= ~SI to PORTB &= ~(1<<SI).

This may or may not work depending on how you have CS, SI and SCK defined ( you decided not to show us these defines)

Do the same for SI and SCK  changing them to (1<<SI) and (1<<SCK)

This way your are only changing the single bit of the port and not setting/clearing the other bits. 

A logic analyzer would be a very useful piece of gear to use in a case like this.

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I use macros for my setting and clearing of bits, so I can use same code in different implementations without have to look for all places port/pin changes are needed.

I am using a AD5293 pot at 5V (so no delays are needed):

 

#define CLR_SCLK (PORTB &= ~(1 << PB1))
#define SET_SCLK (PORTB |= (1<< PB1))
#define CLR_SYNC (PORTB &= ~(1 << PB0))
#define SET_SYNC (PORTB |= (1<< PB0))
#define CLR_DOUT (PORTB &= ~(1 << PB5))
#define SET_DOUT (PORTB |= (1<< PB5))

void wr_pot(uint16_t data){
    CLR_SYNC;
	for (uint8_t i = 16; i>0; i--){
		SET_SCLK;
		if ((data & (1<<(i-1))) != 0) {
			SET_DOUT;
		} else {
			CLR_DOUT;
		}
		CLR_SCLK;
	}
	CLR_DOUT;
	SET_SYNC;
}

 

David (aka frog_jr)

Last Edited: Fri. Jun 17, 2016 - 01:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Frogy,  Nice, I've captured that for my library of code snippets!

 

Jim

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:
if ((data & (1<<(i-1))) != 0)

Just a hint:  With the quoted expression, you are forcing the compiler (in all probability--perhaps your compiler's optimizer is smart enough...) to do a variadic shift each time through the loop.  Consider changing to OP's "test the same bit each time and shift the operand one place each loop to place the next bit in the target location".  The shift direction and bit tested will depend on data order.

 

#include <avr/io.h>
#define CLR_SCLK (PORTB &= ~(1 << PB1))
#define SET_SCLK (PORTB |= (1<< PB1))
#define CLR_SYNC (PORTB &= ~(1 << PB0))
#define SET_SYNC (PORTB |= (1<< PB0))
#define CLR_DOUT (PORTB &= ~(1 << PB5))
#define SET_DOUT (PORTB |= (1<< PB5))

void wr_pot(uint16_t data){
	CLR_SYNC;
	for (uint8_t i = 16; i>0; i--){
		SET_SCLK;
		if ((data & (1<<(i-1))) != 0) {
			SET_DOUT;
			} else {
			CLR_DOUT;
		}
		CLR_SCLK;
	}
	CLR_DOUT;
	SET_SYNC;
}
void wr_potx(uint16_t data){
	CLR_SYNC;
	// LSB first
	for (uint8_t i = 16; i>0; i--){
		SET_SCLK;
		if (data & 1) {
			SET_DOUT;
			} else {
			CLR_DOUT;
		}
		data >>= 1;
		CLR_SCLK;
	}
	CLR_DOUT;
	SET_SYNC;
}
int main(void)
{
	wr_pot(123);
	wr_potx (123);
	while(1)
	{

	}
}

Instructions are a bit hard to follow below, but you'll see the embedded shift loop...

 

00000030 <wr_pot>:
#define SET_SYNC (PORTB |= (1<< PB0))
#define CLR_DOUT (PORTB &= ~(1 << PB5))
#define SET_DOUT (PORTB |= (1<< PB5))

void wr_pot(uint16_t data){
	CLR_SYNC;
  30:	c0 98       	cbi	0x18, 0	; 24
  32:	2f e0       	ldi	r18, 0x0F	; 15
  34:	30 e0       	ldi	r19, 0x00	; 0
	for (uint8_t i = 16; i>0; i--){
		SET_SCLK;
		if ((data & (1<<(i-1))) != 0) {
  36:	61 e0       	ldi	r22, 0x01	; 1
  38:	70 e0       	ldi	r23, 0x00	; 0
  3a:	05 c0       	rjmp	.+10     	; 0x46 <__SREG__+0x7>
			SET_DOUT;
  3c:	c5 9a       	sbi	0x18, 5	; 24
			} else {
			CLR_DOUT;
		}
		CLR_SCLK;
  3e:	c1 98       	cbi	0x18, 1	; 24
  40:	21 50       	subi	r18, 0x01	; 1
  42:	31 09       	sbc	r19, r1
  44:	88 f0       	brcs	.+34     	; 0x68 <__SREG__+0x29>
#define SET_DOUT (PORTB |= (1<< PB5))

void wr_pot(uint16_t data){
	CLR_SYNC;
	for (uint8_t i = 16; i>0; i--){
		SET_SCLK;
  46:	c1 9a       	sbi	0x18, 1	; 24
		if ((data & (1<<(i-1))) != 0) {
  48:	ab 01       	movw	r20, r22
  4a:	02 2e       	mov	r0, r18
  4c:	02 c0       	rjmp	.+4      	; 0x52 <__SREG__+0x13>
  4e:	44 0f       	add	r20, r20
  50:	55 1f       	adc	r21, r21
  52:	0a 94       	dec	r0
  54:	e2 f7       	brpl	.-8      	; 0x4e <__SREG__+0xf>
  56:	48 23       	and	r20, r24
  58:	59 23       	and	r21, r25
  5a:	45 2b       	or	r20, r21
  5c:	79 f7       	brne	.-34     	; 0x3c <wr_pot+0xc>
			SET_DOUT;
			} else {
			CLR_DOUT;
  5e:	c5 98       	cbi	0x18, 5	; 24
		}
		CLR_SCLK;
  60:	c1 98       	cbi	0x18, 1	; 24
  62:	21 50       	subi	r18, 0x01	; 1
  64:	31 09       	sbc	r19, r1
  66:	78 f7       	brcc	.-34     	; 0x46 <__SREG__+0x7>
	}
	CLR_DOUT;
  68:	c5 98       	cbi	0x18, 5	; 24
	SET_SYNC;
  6a:	c0 9a       	sbi	0x18, 0	; 24
  6c:	08 95       	ret

0000006e <wr_potx>:
}
void wr_potx(uint16_t data){
	CLR_SYNC;
  6e:	c0 98       	cbi	0x18, 0	; 24
  70:	20 e1       	ldi	r18, 0x10	; 16
  72:	06 c0       	rjmp	.+12     	; 0x80 <wr_potx+0x12>
	// LSB first
	for (uint8_t i = 16; i>0; i--){
		SET_SCLK;
		if (data & 1) {
			SET_DOUT;
  74:	c5 9a       	sbi	0x18, 5	; 24
			} else {
			CLR_DOUT;
		}
		data >>= 1;
  76:	96 95       	lsr	r25
  78:	87 95       	ror	r24
		CLR_SCLK;
  7a:	c1 98       	cbi	0x18, 1	; 24
  7c:	21 50       	subi	r18, 0x01	; 1
	SET_SYNC;
}
void wr_potx(uint16_t data){
	CLR_SYNC;
	// LSB first
	for (uint8_t i = 16; i>0; i--){
  7e:	49 f0       	breq	.+18     	; 0x92 <wr_potx+0x24>
		SET_SCLK;
  80:	c1 9a       	sbi	0x18, 1	; 24
		if (data & 1) {
  82:	80 fd       	sbrc	r24, 0
  84:	f7 cf       	rjmp	.-18     	; 0x74 <wr_potx+0x6>
			SET_DOUT;
			} else {
			CLR_DOUT;
  86:	c5 98       	cbi	0x18, 5	; 24
		}
		data >>= 1;
  88:	96 95       	lsr	r25
  8a:	87 95       	ror	r24
		CLR_SCLK;
  8c:	c1 98       	cbi	0x18, 1	; 24
  8e:	21 50       	subi	r18, 0x01	; 1
	SET_SYNC;
}
void wr_potx(uint16_t data){
	CLR_SYNC;
	// LSB first
	for (uint8_t i = 16; i>0; i--){
  90:	b9 f7       	brne	.-18     	; 0x80 <wr_potx+0x12>
			CLR_DOUT;
		}
		data >>= 1;
		CLR_SCLK;
	}
	CLR_DOUT;
  92:	c5 98       	cbi	0x18, 5	; 24
	SET_SYNC;
  94:	c0 9a       	sbi	0x18, 0	; 24
  96:	08 95       	ret

Especially with 8-bit data, a walking mask (similar to OP's approach) could well be best.  Certainly makes for simpler code, eliminating the loop counter.

 

// MSB first

for (mask = 0x8000; mask; mask>>=1) { ... }

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Jun 17, 2016 - 03:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

... how you have CS, SI and SCK defined ( you decided not to show us these defines)

+1

 

Now, as I'd usually ask, tell AVR model and why real SPI can't be used.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@thusch

  You are absolutely correct, I had not previously checked my .lss file with that code.

After some initial testing, I had changed the routine to use the hardware SPI and had left this in an abandoned #ifndef block.

 

Edit:

Although the POT needs MSB first rather than LSB so a slight modification is required to operate correctly.

David (aka frog_jr)

Last Edited: Fri. Jun 17, 2016 - 03:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Previously i had given you the example code that i followed to write down my code & here is my actual code:

 

 

//volume step is 0 to 8

//PORTD.4 = CS

//PORTD.5 = SDI

//PORTD.6 = SCL

 

///// INCREMENT VOL FROM BUTTON

void Vol_Ctrl_Inc()
                {
                if (Vol_stp < 8)
                    {
                    Vol_stp++;
                    }
                Vol = (Vol_stp * 64)-1;
                Vol = Vol & 0x03FF;
                send_vol_data(Vol);//wiper-1
                Vol |= (1<<12);
                send_vol_data(Vol);//wiper-2
                
                }
////////////////////////////////////////////////////////////////////

/////DECREMENT VOL FROM BUTTON
void Vol_Ctrl_Dec()
                {
                
                if (Vol_stp > 0)
                    {
                    Vol_stp--;
                    }
                Vol = (Vol_stp * 64)-1;
                Vol = Vol & 0x03FF;
                send_vol_data(Vol);//wiper-1
                Vol |= (1<<12);
                send_vol_data(Vol);//wiper-2
               
                }
/////////////////////////////////////////////////////////////////////
void send_vol_data(unsigned int SL_Data)
                {
                // Start write
                PORTD &= ~(1<<6);//clk low
                _delay_us(200);
                PORTD &= ~(1<<4);//cs low
                _delay_us(200);
                unsigned short mask = 0b1000000000000000; // MSB for Volume

                for(int Dta_Ct=0;Dta_Ct<16;Dta_Ct++)
                    {
                //send data

                    if ((SL_Data & mask) > 0)
                    PORTD |=(1<<5); //data high
                    else
                    PORTD &= ~(1<<5); //data low
                    _delay_us(200);

                    PORTD |=(1<<6);//clk high
                    _delay_us(200);
                    PORTD &= ~(1<<6);//clk low

                    //Shift right
                    mask = mask>>1; // next bit down
                    }
                    PORTD |=(1<<4);//cs high
                    _delay_ms(10); 
                }

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Still, why the large delays?
 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pkd0057 wrote:
I am doing as per Microchip manual but fails repeatedly.

Tell more about that.  Intermittent operation?

 

The device appears to be "smart" w.r.t. SPI mode 0 or mode 3.  For consistent operation, won't you need to set your MOSI-equivalent signal to an appropriate value for your data transfer sequence?

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. well now i am trying to reduce the delay.

2. I am using Atmega162 for my project and i have already used SPI port for my design aspect, otherwise i had a better choice to use SPI port for my controller & i hope there are lots of example with SPI mode.

3. My request to all of you not to suggest me repeatedly using SPI mode.

 

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, Pkdas -

 

SPI is constantly being suggested because you CAN have several peripherals on a single SPI bus. It just takes a select line for each peripheral. And you need to manage the select lines, appropriately (activate the one associated with the device you are talking to). Its very common and not a big deal. It is also significantly  easier to use the built-in hardware. I recently had a project with an accelerometer running at 100Hz (new reading every 10ms) AND a micro-SD memory chip on the same SPI bus with no apparent issues.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pkd0057 wrote:
i have already used SPI port for my design aspect

You realize that you can have multiple SPI slaves on a bus, with separate chip selects.

 

pkd0057 wrote:
3. My request to all of you not to suggest me repeatedly using SPI mode.

I don't quite know what you are asking here.  About using the AVR's SPI peripheral?  (Wasn't I the only one mentioning that?)

 

Or really about SPI >>mode<<?  You still haven't shown your connections, nor your definitions asked for.  Nor what the testing results are.

 

About the delays:  Nothing wrong with slowing things down when developing.  Especially if you have a lash-up such as a breadboard.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

well my controller is atmega 162 & Port B already i am using for DAC 8 bit data , as well as ISP port  for future use.

& so that i cannot use SPI.

 

i have just now tested the code with way Theusch suggested but not working.

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pkd0057 wrote:
i have just now tested the code with way Theusch suggested but not working.

I was only suggesting an alternate method of selecting the needed bit value each pass.

 

theusch wrote:
You still haven't shown your connections, nor your definitions asked for. Nor what the testing results are.

 

Your responses seem more like objections to us readers, rather than considering what is being asked for and providing it.

 

pkd0057 wrote:
as well as ISP port for future use.

If you do not have an ISP port, then how are you programming your microcontroller?

 

(Just because you use those pins for ISP does not mean that they cannot be used for connection to an SPI peripheral.)

 

How have you made your connections?  When you look at the pins at the target device with a 'scope, do the signals look clean and change state as expected?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is the circuit i am using

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

read MCP4251 instead of 4241 as 4252 was not available in lib of drawing s/w

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:
how you have CS, SI and SCK defined ( you decided not to show us these defines)

 

Please show a complete test program as requested.  Or, at least, the defines asked for.  Show us the code you are now using.  Tell what you expect to happen, and what >>is<< happening.

 

(the schematic doesn't really help without those #defines..)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

post no #09 in this article already given the actual code & i do not think u really need a #define anything  where direct set/reset bit command (e.g. PORTD &= ~(1<<4);//cs low) already provided!

define ing is really needed every time for testing code every time?

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Except PD4 goes to J8

PD5 goes to SCL

PD6 goes to SDI

CS is GNDed

 

Edit: changed SDA to SDI above.

David (aka frog_jr)

Last Edited: Fri. Jun 17, 2016 - 07:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:

Except PD4 goes to J8

PD5 goes to SCL

PD6 goes to SDI

CS is GNDed

 

Edit: changed SDA to SDI above.

 

 

Frog_jr.

I have corrected that one , Now i am using as per code

PD4 for /CS

PD5 for SDI

PD6 for SCL

 

Drawing is little bit older

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Perhaps WP (pin 11) needs to be pulled high?
Edit:

and SHDN (pin12)

David (aka frog_jr)

Last Edited: Fri. Jun 17, 2016 - 07:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

shdn i made pull high

what is WP i have seen it in pin conf but not details

i will pull it high 

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

WP is (typically) Write Protect.

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

/Shdn  & /WP pin pulled high & Code tested ----- no result

Pkdas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear All 

Finally i used SPI for this project & dropped the idea of not using SPI.

I have done a lot of work & failure i changed the controller to 2560.

Pkdas