Programming a peanut butter mill.
Tags: beginners, microcontroller, assembly, 8051
Published: 2019-09-23 20:56:10

---
title: Programming a peanut butter mill.
published: true
description: 
tags: beginner, microcontroller, assembly, 8051
---

#Introduction
One of the end-of-year projects given to us by our microcontroller teacher was creating the technical specifications for a peanut butter mill powered by an 8051 microcontroller as well as writing the program that was going to run on the latter (Simulated as we were not going to build the actual mill). 

# Hardware
The fictional mill should have a self-check process before starting and allows an optional second pass during milling. It should be able to self-clean at the end of the milling. Very much like this one, but where the container is enclosed as well as an additional container inside for the optional pass.
![Peanut Butter Mill](https://thepracticaldev.s3.amazonaws.com/i/g60622upi9wq7d02c7tc.jpg)

# Requirements
After pressing the start button, the program should check that at least 1/4 of the first container (The one containing the peanut) is filled and that the last container is empty. If the latter is not, it should turn on a warning light. When all these conditions are met, the milling process starts and the peanut butter is poured in the second container to be milled if the user has chosen to do two passes or the final container if he has chosen only one pass. The first motor stops when the first container is empty and the second motor will stop 5 minutes later if it was running

The self-clean process is very simple. The first 2 containers are filled with water and start both motors. After 3 minutes, they are stopped and the containers are emptied into the final container. This is repeated one more time and the machine is reset.

# Design
Even if we are not building the actual mill, we need to design the interfaces to the microcontroller. The 8051 microcontroller has 4 byte-sized I/O ports. We choose to use 2 for the purpose of this program. 

## Inputs
We have three switches and 3 sensors for the content's level of the containers. Two of the sensors are 2 bits wide because we needed 3 states:
 - 00 -> Empty
 - 01 -> 1/4
 - 11 -> Full

The sensor for the final container is 1 bit, we only need to detect if it's empty. The three switches are
 - S -> On if the machine is started
 - A -> On if the cleaning process should be started
 - M -> On if two passes are requested

## Outputs
All 3 outputs are control bits. They are
- Mx -> Off if the motor x should be running
- P -> Off to fill the containers with water.

#Implementation
As for the implementation process, we have used the [edsim51[(https://edsim51.com) for simulating the program we wrote. We have not used interrupts because it will only make it more complex. If it was a real machine, we would have to make it more asynchronous. A lot of looping as used when the machine was idling and when using the timers in the microcontroller.

## Setup and Verification

We start by setting the timer counter to be a 16 bits number (needed for later. We then read port 2 (inputs). We check if the final container is empty by reading its bit and light the warning light if it is set. if it's not, we check the first bit of sensor 1 (it's on if the first container is 1/4 full or more) and the start switch. We loop this part until all conditions are met.

```armasm
mov tmod, #10h ; set timer 1 to 16bit
verif:
mov p2, #0ffh ; set p2 as input
mov acc, p2; read p2 into the acc
jb 0e4h, ll; jump if first bit of sensor 1 is not set (empty)
anl acc, #41h; isolate s and sensor 1 bits
xrl acc, #41h ; acc will clear if s and sensor 1 first bit is set
jz lm1 ; jump if acc is zero
jmp verif; loop verification
ll:
clr p1.0 ; light warning led
jmp verif ; loop verification
```

## Milling

Milling is very simple. We launch the first motor first. If two passes were selected (M is on), we wait until the second container is at least 1/4 full. Then, another looping until the first container is empty.

```armasm
lm1:
clr p1.4 ; launch motor 1
jnb p2.5, f1 ; jump if clapet is not on
jnb p2.2, $; loop until sensor 2 says it's at least 1/4
clr p1.5 ;launch motor 2 if clapet is on
f1:
jb p2.0, $ ; loop until sensor 1 says empty
```

As soon as the sensor reports that the container is empty, we stop the first motor, then wait 5 seconds (For the purpose of the simulation, we used seconds instead of minutes) to stop the second one. Then we loop until the clean process is instantiated (The switch A is on).

```armasm
setb p1.4 ; stop motor 1
mov r0, #10 ; 5 seconds (5 * 20)
call secondsDelay ; delaying
setb p1.5 ; stop motor 2
jnb p2.7, $ ; loop until a is set
```

## Cleaning

First, we open the water pump and check until that the first 2 containers are full to close it. Then we start both motors, wait 3 seconds, then stop them. This process is repeated one more time, then we stop everything.

```armasm
mov r7, #2;  2 cycle
clean:
clr p1.7 ; open pump
checkEmpty:
mov acc, p2 ; load port 2
anl acc, #0fh ; isolate sensor 1 & 2
xrl acc, #0fh ; clear if all is set
jnz checkEmpty; loop until full
setb p1.7 ; close pump
clr p1.4; start motor 1
clr p1.5 ; start motor 2
mov r0, #10 ; 3 seconds (3 * 20)
call secondsDelay
setb p1.4 ; stop motor 1
setb p1.4 ; stop motor 2
djnz r7, clean
jmp finish
```

## Timer
We decided to use the timer imperatively instead of via interrupts. We implemented it via a procedure. As the timer uses a 16 bits counter, we can only set it for a duration up to 65 536 us. We decided to go with 50 ms in and loop it to attain the expected duration. The number of loops is set in the `r0` register (1 byte) before calling the timer subroutine.

```armasm
; Setup Timer
fiftyMsDelay:
clr tr1 ; stop timer
; set up a 50 000 seconds delay by loading 65 536 - 50 000 = 15 536 (3CB0)
mov th1, #3ch
mov tl1, #0b0h
setb tr1 ; start timer
jnb tf1, $ ; loop until timer oveflow
clr tf1 ; clear overflow
clr tr1 ; stop timer
ret
secondsDelay:
; save processor state
push psw
loopTimer:
call fiftyMsDelay ; 50 ms
djnz r0, loopTimer ; loop until r0 equal zero
; restore processor state
pop psw
ret
```

# Improvements
Our code is very simple and makes the milling process a very straightforward one. But in the real world, there are often edge cases that the programs would have to handle. Like whether the motor is actually running or pausing the milling process.

# Conclusion
Writing this was very stressful, but enjoyable as we have not had much experience writing assembly code. But we learn a lot during the implementation and it had become easier over time. Assembly is actually very simple, but also tedious to write. Next step will probably be porting this to Arduino.