MCP41010 with an ATMEGA168

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

Hey,

I want to write a value to my MCP41010 digital potentiometer in combination with an ATMEGA168 microcontroller. Unfortunately, the code doesnt work, could anybody help me?

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include "pindefines.h"
#include "macros.h"
#include "USART.h"

#define DIPOT_NONE		0b00000001 //geen commando wordt uitgevoerd, begin van de commando byte-sequence. 
#define DIPOT_WRITE		0b00010001 //de data in de databyte worden gezonden naar de bijbehorende potmeters.
#define DIPOT_SHUTDOWN	0b00100001 //de potmeter wordt in shutdown-mode gezet. 
#define DIPOT_NONE_2	0b00110001 //geen commando wordt uitgevoerd, einde van de commando-byte-sequence. 

int value = 90;

void SPI_master (void){
	DDRB = (1<<5) | (1<<3);
	SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0);
}
	
unsigned char SPI_send_data (unsigned char data){
	SPDR = data;
	while(!(SPSR & (1<<SPIF)));
	return(SPDR);
}

int main(void){
	SPI_master();
	unsigned char data;
	PORTB = (1 << PB0);

	while(1){
		PORTB = (0 << PB0);
		data = DIPOT_WRITE;
		SPI_send_data(data);
		data = value;
		SPI_send_data(data);
		data = DIPOT_SHUTDOWN;
		SPI_send_data(data);
		_delay_ms(100);
		PORTB = (1 << PB0);
	}
}

 

Last Edited: Mon. Sep 14, 2020 - 10:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

yesboi wrote:
the code doesnt work,

First welcome to AVRFreaks!

 

Doesn't work is not much of a problem report!!

Start by telling us what you expected the code to do, and then what it is actually doing.

Telling us how you have troubleshot it so far would be helpful as well.   i.e. I used a logic analyzer and I see this.....   or my scope shows this.....   post pictures so we can see it too.

when talking about non Atmel devices, it also helps to post a link to the datasheet of the device so we don't have to go look for it.

 

Jim

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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


If:

		PORTB = (0 << PB0);
...
		PORTB = (1 << PB0);

is your attempt to control a "slave select" (chip select) for the Pot then a couple of things:

 

1) I don't see anywhere in the code where you make bit PB0 an output all I see is the code in SPI_master where you just make bits 5 and 3 outputs.

 

2) (0 << PB0) is not the way to set the bit low. You need to AND the xisting PORTB contents to set any bits low so it would look something more like:

PORTB &= ~(1 << PB0);

and if you don't understand that syntax go to the Tutorial Forum here and find the post about "bit manipulation 101"

 

3) PB0 is not a good pin to pick for SS anyway. There is a "gotcha" in SPI for AVR in that there is a pin set aside to be used as SS and if you don't use that one and just leave the pin unconnected then if it happens to read "LOW" then the SPI will not work (it will turn from MSTR to slave operation). So pick the designated SS pin for the slave select to the 41010

 

EDIT: datasheet says that the SS pin is PB2...

 

Last Edited: Mon. Sep 14, 2020 - 02:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Thanks for your reply! I'm new to AVR so thanks for the suggestion of how to set a pin low. I changed the SS pin to PB2. The digital potentiometer will be used as the slave. I wired the wiper pin to a multimeter and i changed the integer value to various values. Unfortunately the multimeter gives me an overload. Are there any things i'm missing in my code?

 

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


Some additional info:

From what i understand from the datasheet i'll have to give a command byte to the slave. The write command is defined in the top part of the code. I used this format:

 

The following picture shows the events that the device needs to change the resistor value

 

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

yesboi wrote:
I changed the SS pin to PB2.

That's fine, but you may have missed that PB2 needs to be an output or at least enable the internal pull up on PB2 to prevent the gottcha that bites most SPI beginners as Cliff pointed out.

 

Jim

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Yes, i did that too. I'll make this a good habit from now on haha. 

	DDRB = (1<<5) | (1<<3) | (1<<2);

 

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

Lets make your life a little easy:

 

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include "pindefines.h"
#include "macros.h"
#include "USART.h"

#define DIPOT_NONE		0b00000001 //geen commando wordt uitgevoerd, begin van de commando byte-sequence. 
#define DIPOT_WRITE		0b00010001 //de data in de databyte worden gezonden naar de bijbehorende potmeters.
#define DIPOT_SHUTDOWN	0b00100001 //de potmeter wordt in shutdown-mode gezet. 
#define DIPOT_NONE_2	0b00110001 //geen commando wordt uitgevoerd, einde van de commando-byte-sequence. 


void spi_send(uint8_t8 epot_data)
{
    
    SPDR0 = epot_data;             //send the data
    while(!(SPSR0 & (1<<SPIF0)));  //wait till finished
    
    
}


void avr_inint(void)
{
    // Input/Output Ports initialization
// Port B initialization
// Function: Bit7=In Bit6=In Bit5=Out Bit4=In Bit3=Out Bit2=Out Bit1=In Bit0=In 
DDRB=(0<<DDB7) | (0<<DDB6) | (1<<DDB5) | (0<<DDB4) | (1<<DDB3) | (1<<DDB2) | (0<<DDB1) | (0<<DDB0);
// State: Bit7=T Bit6=T Bit5=0 Bit4=T Bit3=0 Bit2=0 Bit1=T Bit0=T 
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 1000.000 kHz
// SPI Clock Phase: Cycle Start
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=(0<<SPIE) | (1<<SPE) | (0<<DORD) | (1<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (1<<SPR0);
SPSR=(0<<SPI2X);
    
}


void main(void)
{
    delay_ms(500);
    avr_init();    //initialise the chip
    
    //set teh epot to one end of teh pot travel
    PORTB = PORTB & ~(1<<PORTB4);    //enable teh POT
    spi_send(DIPOT_WRITE);   //pick teh pot and the function
    spi_send(0);    //write the location - in this cse at teh one end of the pot
    PORTB = PORTB | (1<<PORTB4);  //deselect the chip
    
    //now have teh EPOT wiper move from one end to teh other and back
    while(1)
    {
        //going up
        for(uint8_t i = 0; i < 255; i++)
        {
            PORTB = PORTB & ~(1<<PORTB4);    //enable teh POT
            spi_send(DIPOT_WRITE);   //pick teh pot and the function
            spi_send(i);    //write the location - 
            PORTB = PORTB | (1<<PORTB4);  //deselect the chip
            
            _delay_ms(100); //hang out
        }
        
        //going down
        for(uint8_t i =255; i > 0; i--)
        {
            PORTB = PORTB & ~(1<<PORTB4);    //enable teh POT
            spi_send(DIPOT_WRITE);   //pick teh pot and the function
            spi_send(i);    //write the location - 
            PORTB = PORTB | (1<<PORTB4);  //deselect the chip
            
            _delay_ms(100); //hang out
        }
    }
}

Completely untested, but I am pretty sure it will work.

 

Now when you run this code, put your meter from PW0 to PA0 and you should see the meter readings increment, or decrment depending on where the code is.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

yesboi wrote:
I'm new to AVR so thanks for the suggestion of how to set a pin low

Note that what clawson said is not specific to AVR - it would apply to any microcontroller.

 

clawson wrote:
(0 << PB0) is not the way to set the bit low.

It will actually set all the bits in the entire port low. As he said, what you actually need is to set just the one bit low - and leave the others alone.

 

clawson wrote:
go to the Tutorial Forum here and find the post about "bit manipulation 101"

Here: https://www.avrfreaks.net/forum/tut-c-bit-manipulation-aka-programming-101

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, i did that too. I'll make this a good habit from now on haha. 

	DDRB = (1<<5) | (1<<3) | (1<<2);

No that is not a good habit.

Define each pin with a useful name so it is clear: 

 

DDRB = (1<<ALARM_RED_LED)  |  (1<<TRAPDOOR_RELEASE) | (1<<BUZZER_EN)  //outputs listed 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

avrcandies wrote:
Define each pin with a useful name so it is clear

+99

 

You might find it even clearer to have a definition of the bit number, plus a definition of the bit mask; eg

// Bit Numbers
#define ALARM_RED_LED_NUM    5
#define TRAPDOOR_RELEASE_NUM 1
#define BUZZER_ENA_NUM       3

// Bit Masks
#define ALARM_RED_LED_MASK    (1<<ALARM_RED_LED_NUM   )
#define TRAPDOOR_RELEASE_MASK (1<<TRAPDOOR_RELEASE_NUM)
#define BUZZER_ENA_MASK       (1<<BUZZER_ENA_NUM      )

 

Then you don't have to keep manually re-typing the "(1<<...)" stuff; eg,

DDRB = ALARM_RED_LED_MASK  |  TRAPDOOR_RELEASE_MASK | BUZZER_EN_MASK

Which seems a lot less "cluttered" to me.

 

This is the way Atmel Microchip do it in more recent stuff ... 

 

EDIT 

 

see: https://www.avrfreaks.net/commen...

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Tue. Sep 15, 2020 - 05:50 PM