ATMega128 Coffee Capsules Dispenser Project

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

I was wondering whether or not I should create a new post about this project (you can read it's introduction here). In my previous one I recieved lots of feedback from other programmers and I learned quite a lot. After (briefly) reading the instruction manual for the ATMega128 and breaking up the project in simple parts I managed to understand the exercise better and did some lines of code (attached to this post). 

 

I failed at doing a state diagram physically because my mind gets blown away by the ammount of loops and redirections that the program suffers but I managed to understand the flow of the project. 

 

At the moment I'm currently stuck:
 

  • I'm failing to understand how to define a max value for a register. (In this case, I'm only considering the maximum value of capsules as 9). The in instruction isn't corrent nor is the ldi one (from what I've read on the manual).
  • I'm also failing to figure out how to read a constant value from a register. (In this case, I'm considering r22 as the place where I'll store the number of capsules at the current time).

 

Besides that, the code is pretty solid at the moment. It's pretty far away from the final thing but it's more than nothing. In my opinion, if I understand how to read the register value, I can proceed with the code becasue (even though the commentaries are in portuguese) the code is basically dependent on the "what's the number in r22" as I'm implementing conditions on my labels. 

 

For now that's it. Hopefully the feedback will be great once again and I can learn even more. I know it's probably a very basic code but I enjoyed it actually smiley!

 

(I'm without time as I have other projects to do but if it's needed I can translate what's in the commentaries. I don't think it is anything important besides explanations for me to know what I'm doing).

 

EDIT1: Added english translations.

Attachment(s): 

Last Edited: Sat. Oct 13, 2018 - 02:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your comments are in Portuguese. Not everyone can read that, maybe you should write an international version?

 

edit: the instructions you need to check register values are the compare instructions, like CP and CPI.

Last Edited: Fri. Oct 12, 2018 - 09:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

edit: the instructions you need to check register values are the compare instructions, like CP and CPI.

Followed by appropriate branch instructions.

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

What do you mean by that? Didn't understand I'm sorry. I'll update the main post after translating.

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

Regarding how to create variables in asm, well, this project is simple so maybe all variables can be kept in registers.

But in case RAM variables are needed, here is an excerpt from a program where I needed some RAM variables, they go in the .dseg memory space (RAM). It also has a 7-segment character map, similar to what you will need eventually. These are constants, so they go to the same segment as code .cseg (flash memory).

 


 ;character maps for 7-segment display
.EQU	char_0 = 0b00111111
.EQU	char_1 = 0b00000110
.EQU	char_2 = 0b01011011
.EQU	char_3 = 0b01001111
.EQU	char_4 = 0b01100110
.EQU	char_5 = 0b01101101
.EQU	char_6 = 0b01111101
.EQU	char_7 = 0b00000111
.EQU	char_8 = 0b01111111
.EQU	char_9 = 0b01101111
.EQU	char_A = 0b01110111
.EQU	char_B = 0b01111100
.EQU	char_C = 0b00111001
.EQU	char_D = 0b01011110
.EQU	char_E = 0b01111001
.EQU	char_F = 0b01110001

.CSEG

;...
;code
;...

char_data:
.DB char_0, char_1, char_2, char_3, char_4, char_5, char_6, char_7, char_8, char_9
;hexadecimal
.DB char_A, char_B, char_C, char_D, char_E, char_F

.DSEG
.ORG SRAM_START
display_result: .DW 0
buffer: .BYTE 78		;reserve 39 words for circular buffer
current_sum: .DW 0
buffer_index: .DB 0

 

Notice that I removed all actual asm instructions, but there is still plenty, mostly asm directives that you will also need to learn:

https://www.microchip.com/webdoc...

 

They are specific for each assembler, but it seems you are using the AVR assembler from Atmel.

 

edit: I forgot to say - variables that you create in RAM are not initialized, they contain random values. So, even though I write .DW 0, .DB 0 etc., they don't have zero in general.

Last Edited: Fri. Oct 12, 2018 - 10:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I kinda understood what you said but at the same time I didn't. It's hard to explain because I really haven't learned how to create variables in the RAM. I suppose I could do that in my project but I assume that I can do it with the things I learned. You talked about beeing so simple that I could create variables in registers. Would you care to explain that more?

 

In my mind I was going to use r22 (a random register I guess) to increment it and decrement it. So, for example, it started by having 0 capsules. As I pressed the Increment button, it would increment one, and so on and so on. My only trouble is reading the number in the register at the beggining (as I'm not comparing it to nothing how can I read it?). 

I reckon that it would be correct to do for example:

 

CPI r22,9 to see if the maximum capacity was reached but that's my point, how do I max out the capacity (so if he presses the button again it simply breaks the code back to the beggining) and how can I read it right in the beggining (since im not comparing it to anything, my idea is just to store that value in the variable / r22).

 

Also, I've updated the OP so the code now has english translations on the comments.

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

What you can do is compare to 9, then jump over the inc instruction if it's equal (with breq):

 

    cpi r22, 9
    breq skip
    inc r22
skip:
    jmp start

 

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

I understand what you are saying but I'm doing the other way around. By this I mean: 

In the full project, the number of maximum capsules can change. With this, I'm guiding my increment function by the Led being turned on or not (which will then make me skip the inc instruction) and not guiding myself through the number in particular. Do you understand?

 

It's probably the same but I guess I simply prefer it like this. It seems more clear to me.

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

Nothing to do with Studio issues or bugs, moving to AVR forum.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Jonaas18 wrote:
Also, I've updated the OP so the code now has english translations on the comments.

Please do not modify your op code anymore. When you make changes repost them in a reply post. This way anyone reading the thread will understand what has transpired.

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

 

"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 user

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

I didnt modify. I just translated the commentaries so it would be easier for New people to read it. Anyways thanks a lot for the explanation and I will be careful next time around.

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

Alright, I managed to increase my code (adding the increment/decrement labels and turning the led on when it reaches max capacity) but I'm stuck on some lines.

 

I need to make 2 readings to see if the person pressed the same button. If she did, the max capacity would change. For example: If she pressed Switch 1, I would read it, call a delay function and then branch to another label where I would read again and see if the switch was pressed or not. If it wasn't, it's supposed to fail and jmp to the beggining. I'm having trouble checking whether she clicked on the switch or not. It's such a basic thing and I've literelly done it in other exercises but I'm blowing my mind wondering if I should use (In ... ANDI .. BREQ) or (SBIS ... BREQ).

 

Besides that I'm still clueless on how to define the "Maximium number of capsules". For example, by default it is 9 so how do I even write it down on code? How can I force r22 (where I'm storing my capsules number) to have values between 0 - 9?

 

Code:

 

Leitura:			if SW1 pressed
						Call Delay
						breq SW_1

					if SW2 pressed
						Call Delay
						breq SW_2

					if SW3 pressed
						Call Delay
						breq SW_3

SW_1:				if SW1 pressed
					  Max capsules number = 4
					jmp Start

SW_2:				if SW2 pressed
						Max capsules number = 6
					jmp Start

SW_3:				if SW3 pressed
						Max capsules number = 9
					jmp Start

 

I've really came far to where I was in the beggining. It may seem nothing but I've already done lots of things that I had no clue were so easy. Thanks a lot for the help!

Last Edited: Sat. Oct 13, 2018 - 06:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:

Besides that I'm still clueless on how to define the "Maximium number of capsules".
 

 

You can put this in another register, there are plenty of them. You can give names to registers with .def to make it easier to understand the code:

 

.DEF max_caps=r16
.DEF current_caps=r22

 

After these directives you can use the new names instead of r16 or r22.

 

Regarding the buttons, you can for example connect all the buttons to the same port, like PORTA and wait in a loop for any changes. Then, you can check which button was pressed.

The best way to use the buttons is to enable the pullup resistors, so the input will be 1 when the button is inactive. The buttons are connected to GND, so when you press it, it goes to zero.

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

Yea but for example, that's just naming registers, not putting a max value in them? I guess that's not possible.. I'm not sure. I've made more updates, I'll post the whole code since it's easier I guess. I'm still having doubts on the jmp xxxx parts at the end because it seems like a very messy and confusing project but I think it's going well:

 

.include <m128def.inc>

Inic:				        ldi r16,0xff					; Initializing pointer
					out spl,r16					; of stack (0x10FF).
					ldi r16,0x10
					out sph,r16 

					clr r22					        ; Clear register22. It's where I'll place the number of current capsules.
					clr r23						; Clear register23. It's where I'll place the number of max capsules.

					ldi r16,0b11000000
					out DDRD,r16					; Pins 1-6 defined as 0 as they are inputs (Switches 1-6). Pins 7 and 8 are defined as 1 as they are the ones to choose the furthest to the right number display (I haven't really touched on this yet).
					ldi r16,0xff
					out DDRA,r16					; All pins defined as 1 as they are outputs (LED).

Start:				        in r16,PINA
					andi r16,0b11111110				; If switch1 is pressed, branches to label SW_1.
					breq SW_1

					in r16,PINA
					andi r16,0b11111101				; If switch2 is pressed, branches to label SW_2.
					breq SW_2

					in r16,PINA
					andi r16,0b11111011				; If switch3 is pressed, branches to label SW_3.
					breq SW_3

					jmp Main					; If none of the above is pressed it will simply skip to the Main label. Necessary???

SW_1:				        call Delay					; Calls the delay function (so it has 1.5ms waiting time between each read)
					in r16,PINA
					andi r16,0b11111110				; If switch1 is pressed again, register 23 is changed
					ldi r23,4					; to have the number 4, meaning max capsules = 4.
					jmp Start

SW_2:				        call Delay					; Calls the delay function (so it has 1.5ms waiting time between each read)
					in r16,PINA
					andi r16,0b11111101				; If switch2 is pressed again, register 23 is changed
					ldi r23,6					; to have the number 6, meaning max capsules = 6.
					jmp Start

SW_3:				        call Delay					; Calls the delay function (so it has 1.5ms waiting time between each read)
					in r16,PINA
					andi r16,0b11111011				; If switch3 is pressed again, register 23 is changed
					ldi r23,9					; to have the number 9, meaning max capsules = 9.
					jmp Start

Main:				        cpi r22,9
					breq LigaLed

					in r16,PIND
					andi r16,0x05
					breq Incrementar                               ; If switch5 is pressed, it will branch to the label Incrementar.

					in r16,PIND
					andi r16,0x06
					breq Decrementar                               ; If switch6 is pressed, it will branch to the label Decrementar.
					rjmp Main                                      ; rjmp to create a loop.

Incrementar:		                cpi r22,9
					breq Skip                                      ; If the number of capsules is = 9, it can't increment more so it breaks the code.
					inc r22                                        ; Increments r22 by 1.
					jmp Start

Decrementar:		                cpi r22,0
					breq Skip                                      ;  If the number of capsules is = 0, it can't decrement less so it breaks the code.
					dec r22                                        ; Drecrements r22 by 1.
					jmp Start

Skip:				        jmp Main

LigaLed:			        ldi r16,0b01111111 ; Turns the LED D8 ON when it reaches max capacity. Have to change so it is not only for when the number is 9, but for when the number is also 4 or 6 (different max capacities).
					out PORTA,r16
					jmp Start


Delay:
				ldi  r18, 32 ; Delay 24 000 cycles
				ldi  r19, 42 ; 1ms 500us at 16.0 MHz  IM STILL WORKING ON THIS, I THINK IT'S WRONG BUT I'LL FIGURE IT OUT EVENTUALLY.
L1:				dec  r19
				brne L1
				dec  r18
				brne L1
				nop

 

At the moment my biggest problems are really the part where I can change the max capacity but I'm kinda figuring it out so yea. My biggest issue is really beeing unsure if the code is working or not. For example, in the part where I branch if they press a switch and then see if they press it again. Is it correct like that? I keep feeling like I need to use the SBIS instruction somewhere there instead of the code I've written. Am I doing right or is it a complete failure in terms of building a solution?

Last Edited: Sun. Oct 14, 2018 - 12:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to be clear, how are the buttons connected, from these 2 options?

 

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

I don't really know .. But i remember my teacher saying something about Pull-up's so I suppose it is like that... What does it mean? Can you explain to help me figure out if I know by other words or if I literelly haven't read anything about it?

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

 

 

Just a few notes to add.

 

When you DEFine something you can do several things:

 

You can DEFine a name to a register:

.DEF   temp=r16

or

you can define a value to a name:

.DEF   Max_num_capsules=10

 

so by doing this you can put the max number of cups many ways:

 

    ldi temp,Max_num_capsules;

 

or you can add the Max_num__cups to ANY register:

        ldi r22,Max_num_capsules;

 

But I think the OP is looking to be able to change the maximum number of capsules by a button press, then store this new number in a register.  SO simply take out a piece of paper and a pencil and WRITE down the steps you need to make this happen

 

if <increase button> is pressed
    number of capsules register = number of capsules register +1
    max capsules register = number of capsules register

if <decrease button> is pressed
    number of capsules register = number of capsules register -1
    max capsules register = number of capsules register

Now for the decrease, you will need to detect when the number of capsules reaches 0 so as to not keep subtracting 1 from zero.

 

Keep in mind that even though you 'have' 32 registers to work with, thats not always the case as I learned the hard way.  Its safer to assume you only have 16(the upper 16) as teh lower ones are sometimes used for other things like math functions.  Unless your coffee maker is that simple that you can get away with using registers only, great, otherwise I would suggest you learn how to use the SRAM to store your variables.

 

Jonaas18 wrote:
that's just naming registers, not putting a max value in them? I guess that's not possible..

Sure it is...I just showed you wink

 

Jonaas18 wrote:
Besides that I'm still clueless on how to define the "Maximium number of capsules". For example, by default it is 9 so how do I even write it down on code? How can I force r22 (where I'm storing my capsules number) to have values between 0 - 9?

Just showed that toosmiley

 

Jonaas18 wrote:
I would read it, call a delay function and then branch to another label where I would read again and see if the switch was pressed or not. If it wasn't, it's supposed to fail and jmp to the beggining. I'm having trouble checking whether she clicked on the switch or not. It's such a basic thing and I've literelly done it in other exercises but I'm blowing my mind wondering if I should use (In ... ANDI .. BREQ) or (SBIS ... BREQ).

 

Be careful with branches and jumps as you could potentially blow the stack.  To check to see if a button was pressed in the simplest of formats READ the PIN register of the port you have the button(s) connected to, and compare the reading to a MASK to see if that button was pressed.  there are several ways this could be checked

 

For example, lets say that you have the button connected to PORTA pin 0, and it is active high, meaning that when you press the button a logic 1 will be read for that pin.  Since PORTA pin 0 has a binary weight of one your MASK would be 'b00000001', or '0x01' in hex

 

in r16, PINA;       //read PINA register into r16
cpi r16, 0x01;      //compare r16 to the bit mask
breq btn_pressed;   //button pressed branch to the function
.
.
.
..
.
.
btn_pressed:
/*write function code here*/

untested.

Keep in mind this only works if all the other pins are at logic 0 when the PIN register is read.  To isolate the one bit, you would use an AND to clear all the other bits to zero leaving the pin you want unaffected.

in r16, PINA;
andi r16, 0x01;      //and immediate bit mask to register to clear all bits other than the first one
cpi r16, 0x01;      //compare teh reading to the mask
breq btn_pressed;   //button pressed branch!
.
.
.
.
btn_pressed:
/*Write function code here*/

untested.

 

Once you learn how to use DEFines, you can create your MASKs at the head of your program where maintaining them will make your life, and others reading your codes life easier.

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

 

"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 user

Last Edited: Sun. Oct 14, 2018 - 02:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Woah mate you literelly answered all my questions. It is a bit late and I am already with my computer turned off but reading what you wrote briefly I understood A LOT, specially the switch pressed or not part. I will read it carefully tomorrow morning and update my code aswell but before anything else, thanks a lot for the time and effort. Really made my day to understand these things which may be simple but in my head they get mixed up.

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

One of the freaks here told me a long time ago....Use your PC and stylus(not the computer and mouse) to work out your problem BEFORE you sit at the keyboard and start typing.  By breaking things down on the PC and stylus you now have a blueprint of what you want to do and can attack each part individually, then connect them together.

 

Jim

 

 

PC = Paper Computer

Stylus = Pencil

 

You MUST use a pencil.  Pens don't work so well as they do not erase boo boos cleanly.

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

 

"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 user

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

It is said a picture is worth 1000 words... so this is how a button with pull-up works:

 

The pull-up resistor is internal to the MCU, but it's not enabled by default, you need to do it in the program. When the button is not pressed, the I/O pin is connected to VCC via the resistor, so it reads logic value 1.

When you press the button, it gets shorted to GND, so the I/O pin will read logic zero.

 

So in the program you need to check:

pin reads 1: button off

pin reads 0: button on

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

Yes I think it is supposed to be like that. When it is 0 it is pressed, when it is 1 it is not.

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

My problem is that I find it harder to design a flow chart or something identical for this project because it has so many loops. I can't explain but it's not like "1st stage here, then here, then here, and then loop to the beggining". It has so many loops after every stage that my mind gets confused tbh.

 

About the switch pressed or not: 

 

The way you teached with the code example made me understand that it is no big deal, tho I still have a question. Imagine SW1 is pressed:

 

in r16,PINA
andi r16,0x01

This compares the switch to the mask and checks if it was pressed or not. Imagine it was. What happens then? Does it work like the SBIS/SBIC instructions?

 

in r16,PINA
andi r16,0x01
ldi r23,4

The button was pressed so the code moves on and the register gets the value 4 assigned to it. What if it wasn't? Would the code still assign the constant to the register or would it skip that instruction?

 

Sorry for having such a hard time figuring this out.

Last Edited: Sun. Oct 14, 2018 - 01:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:

(...) Imagine SW1 is pressed:

 

in r16,PINA
andi r16,0x01

This compares the switch to the mask and checks if it was pressed or not. Imagine it was. What happens then? Does it work like the SBIS/SBIC instructions?

 

The andi instruction will change the flags to reflect the result. If the button is pressed, the result will be zero, so the Z flag will be set. If not, the result is non-zero so Z will be zero.

 

You need to read the instruction manual carefully, some instructions will change the flags (usually arithmetic and logic instructions), others will not (like ldi, mov and other data transfer instructions), and others will act according to flag state.

The most important instructions that act on flag states are the conditional branches, so after the andi instruction, you execute a branch based on the Z flag. From the instruction manual:

 

BREQ - Branch if Equal

Description:

Conditional relative branch. Tests the Zero flag (Z) and branches relatively to PC if Z is set.

BRNE - Branch if Not Equal

Description:

Conditional relative branch. Tests the Zero flag (Z) and branches relatively to PC if Z is cleared.

 

 You can also use the skip instructions, but they are different, they don't use the flags. Even cpse does a compare but the flags are not touched.

They work by testing some condition, an skip one instruction if the condition is met.

 

So, for example:

sbis    PINA, 0
jmp     btn_pressed     ;if the button is not pressed, bit zero is 1 so this instruction is skipped over

 

 

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

Alright.

 

Went into a coding marathon and I managed to finish the project (due to tomorrow). I really have no way to test it out since I don't have an ATMega128 micro nearby nor do I know how to work with the Debug Section. However, I built the code and there was no errors. Doesn't mean that much but still, would be worst if the code wasn't even properly written laugh!

 

I don't know if it will help anyone since it's such a specific exercise but I'll still post it in the attachments section so anyone who needs something identical can check it out (although, once again, I'm not 100% sure I've done everything right). Also, the comments are in Portuguese but if anyone wishes I can translate it to English when I have the time. I suppose it's up to anyone if they want to open the code and check it out or simply leave it. 

 

Thanks to everyone who participated in this discussion and spared their time to help me with such trivial things. It was harsh to get used to the assembly language but you guys for sure made it easier! I learnt a lot from coding myself and from everyone's replies. Thank you.

 

João

Attachment(s): 

Last Edited: Mon. Oct 15, 2018 - 12:28 AM