gabriel becker
2 years ago
commit
763e5fbe51
4 changed files with 389 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||||||
|
# references |
||||||
|
- https://www.circuitschools.com/interfacing-16x2-lcd-module-with-raspberry-pi-pico-with-and-without-i2c/ |
||||||
|
- Interrupts [ref](https://docs.micropython.org/en/latest/library/machine.Pin.html#machine.Pin.irq) \ |
||||||
|
Pin.irq(handler=None, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, *, priority=1, wake=None, hard=False) |
||||||
|
``` python |
||||||
|
SW3 = Pin(("GPIO_0", 4), Pin.IN) |
||||||
|
SW3.irq(lambda t: print("SW3 changed")) |
||||||
|
``` |
||||||
|
- [Using flash memory as non-volatile storage on the Pi Pico microcontroller](https://kevinboone.me/picoflash.html?i=2) |
||||||
|
- https://pypi.org/project/littlefs-python/ |
||||||
|
- [buzzer reference](https://www.tomshardware.com/how-to/buzzer-music-raspberry-pi-pico) |
@ -0,0 +1,212 @@ |
|||||||
|
"""Provides an API for talking to HD44780 compatible character LCDs.""" |
||||||
|
|
||||||
|
import time |
||||||
|
|
||||||
|
class LcdApi: |
||||||
|
"""Implements the API for talking with HD44780 compatible character LCDs. |
||||||
|
This class only knows what commands to send to the LCD, and not how to get |
||||||
|
them to the LCD. |
||||||
|
|
||||||
|
It is expected that a derived class will implement the hal_xxx functions. |
||||||
|
""" |
||||||
|
|
||||||
|
# The following constant names were lifted from the avrlib lcd.h |
||||||
|
# header file, however, I changed the definitions from bit numbers |
||||||
|
# to bit masks. |
||||||
|
# |
||||||
|
# HD44780 LCD controller command set |
||||||
|
|
||||||
|
LCD_CLR = 0x01 # DB0: clear display |
||||||
|
LCD_HOME = 0x02 # DB1: return to home position |
||||||
|
|
||||||
|
LCD_ENTRY_MODE = 0x04 # DB2: set entry mode |
||||||
|
LCD_ENTRY_INC = 0x02 # --DB1: increment |
||||||
|
LCD_ENTRY_SHIFT = 0x01 # --DB0: shift |
||||||
|
|
||||||
|
LCD_ON_CTRL = 0x08 # DB3: turn lcd/cursor on |
||||||
|
LCD_ON_DISPLAY = 0x04 # --DB2: turn display on |
||||||
|
LCD_ON_CURSOR = 0x02 # --DB1: turn cursor on |
||||||
|
LCD_ON_BLINK = 0x01 # --DB0: blinking cursor |
||||||
|
|
||||||
|
LCD_MOVE = 0x10 # DB4: move cursor/display |
||||||
|
LCD_MOVE_DISP = 0x08 # --DB3: move display (0-> move cursor) |
||||||
|
LCD_MOVE_RIGHT = 0x04 # --DB2: move right (0-> left) |
||||||
|
|
||||||
|
LCD_FUNCTION = 0x20 # DB5: function set |
||||||
|
LCD_FUNCTION_8BIT = 0x10 # --DB4: set 8BIT mode (0->4BIT mode) |
||||||
|
LCD_FUNCTION_2LINES = 0x08 # --DB3: two lines (0->one line) |
||||||
|
LCD_FUNCTION_10DOTS = 0x04 # --DB2: 5x10 font (0->5x7 font) |
||||||
|
LCD_FUNCTION_RESET = 0x30 # See "Initializing by Instruction" section |
||||||
|
|
||||||
|
LCD_CGRAM = 0x40 # DB6: set CG RAM address |
||||||
|
LCD_DDRAM = 0x80 # DB7: set DD RAM address |
||||||
|
|
||||||
|
LCD_RS_CMD = 0 |
||||||
|
LCD_RS_DATA = 1 |
||||||
|
|
||||||
|
LCD_RW_WRITE = 0 |
||||||
|
LCD_RW_READ = 1 |
||||||
|
|
||||||
|
def __init__(self, num_lines, num_columns): |
||||||
|
self.num_lines = num_lines |
||||||
|
if self.num_lines > 4: |
||||||
|
self.num_lines = 4 |
||||||
|
self.num_columns = num_columns |
||||||
|
if self.num_columns > 40: |
||||||
|
self.num_columns = 40 |
||||||
|
self.cursor_x = 0 |
||||||
|
self.cursor_y = 0 |
||||||
|
self.implied_newline = False |
||||||
|
self.backlight = True |
||||||
|
self.display_off() |
||||||
|
self.backlight_on() |
||||||
|
self.clear() |
||||||
|
self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC) |
||||||
|
self.hide_cursor() |
||||||
|
self.display_on() |
||||||
|
|
||||||
|
def clear(self): |
||||||
|
"""Clears the LCD display and moves the cursor to the top left |
||||||
|
corner. |
||||||
|
""" |
||||||
|
self.hal_write_command(self.LCD_CLR) |
||||||
|
self.hal_write_command(self.LCD_HOME) |
||||||
|
self.cursor_x = 0 |
||||||
|
self.cursor_y = 0 |
||||||
|
|
||||||
|
def show_cursor(self): |
||||||
|
"""Causes the cursor to be made visible.""" |
||||||
|
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY | |
||||||
|
self.LCD_ON_CURSOR) |
||||||
|
|
||||||
|
def hide_cursor(self): |
||||||
|
"""Causes the cursor to be hidden.""" |
||||||
|
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY) |
||||||
|
|
||||||
|
def blink_cursor_on(self): |
||||||
|
"""Turns on the cursor, and makes it blink.""" |
||||||
|
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY | |
||||||
|
self.LCD_ON_CURSOR | self.LCD_ON_BLINK) |
||||||
|
|
||||||
|
def blink_cursor_off(self): |
||||||
|
"""Turns on the cursor, and makes it no blink (i.e. be solid).""" |
||||||
|
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY | |
||||||
|
self.LCD_ON_CURSOR) |
||||||
|
|
||||||
|
def display_on(self): |
||||||
|
"""Turns on (i.e. unblanks) the LCD.""" |
||||||
|
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY) |
||||||
|
|
||||||
|
def display_off(self): |
||||||
|
"""Turns off (i.e. blanks) the LCD.""" |
||||||
|
self.hal_write_command(self.LCD_ON_CTRL) |
||||||
|
|
||||||
|
def backlight_on(self): |
||||||
|
"""Turns the backlight on. |
||||||
|
|
||||||
|
This isn't really an LCD command, but some modules have backlight |
||||||
|
controls, so this allows the hal to pass through the command. |
||||||
|
""" |
||||||
|
self.backlight = True |
||||||
|
self.hal_backlight_on() |
||||||
|
|
||||||
|
def backlight_off(self): |
||||||
|
"""Turns the backlight off. |
||||||
|
|
||||||
|
This isn't really an LCD command, but some modules have backlight |
||||||
|
controls, so this allows the hal to pass through the command. |
||||||
|
""" |
||||||
|
self.backlight = False |
||||||
|
self.hal_backlight_off() |
||||||
|
|
||||||
|
def move_to(self, cursor_x, cursor_y): |
||||||
|
"""Moves the cursor position to the indicated position. The cursor |
||||||
|
position is zero based (i.e. cursor_x == 0 indicates first column). |
||||||
|
""" |
||||||
|
self.cursor_x = cursor_x |
||||||
|
self.cursor_y = cursor_y |
||||||
|
addr = cursor_x & 0x3f |
||||||
|
if cursor_y & 1: |
||||||
|
addr += 0x40 # Lines 1 & 3 add 0x40 |
||||||
|
if cursor_y & 2: # Lines 2 & 3 add number of columns |
||||||
|
addr += self.num_columns |
||||||
|
self.hal_write_command(self.LCD_DDRAM | addr) |
||||||
|
|
||||||
|
def putchar(self, char): |
||||||
|
"""Writes the indicated character to the LCD at the current cursor |
||||||
|
position, and advances the cursor by one position. |
||||||
|
""" |
||||||
|
if char == '\n': |
||||||
|
if self.implied_newline: |
||||||
|
# self.implied_newline means we advanced due to a wraparound, |
||||||
|
# so if we get a newline right after that we ignore it. |
||||||
|
self.implied_newline = False |
||||||
|
else: |
||||||
|
self.cursor_x = self.num_columns |
||||||
|
else: |
||||||
|
self.hal_write_data(ord(char)) |
||||||
|
self.cursor_x += 1 |
||||||
|
if self.cursor_x >= self.num_columns: |
||||||
|
self.cursor_x = 0 |
||||||
|
self.cursor_y += 1 |
||||||
|
self.implied_newline = (char != '\n') |
||||||
|
if self.cursor_y >= self.num_lines: |
||||||
|
self.cursor_y = 0 |
||||||
|
self.move_to(self.cursor_x, self.cursor_y) |
||||||
|
|
||||||
|
def putstr(self, string): |
||||||
|
"""Write the indicated string to the LCD at the current cursor |
||||||
|
position and advances the cursor position appropriately. |
||||||
|
""" |
||||||
|
for char in string: |
||||||
|
self.putchar(char) |
||||||
|
|
||||||
|
def custom_char(self, location, charmap): |
||||||
|
"""Write a character to one of the 8 CGRAM locations, available |
||||||
|
as chr(0) through chr(7). |
||||||
|
""" |
||||||
|
location &= 0x7 |
||||||
|
self.hal_write_command(self.LCD_CGRAM | (location << 3)) |
||||||
|
self.hal_sleep_us(40) |
||||||
|
for i in range(8): |
||||||
|
self.hal_write_data(charmap[i]) |
||||||
|
self.hal_sleep_us(40) |
||||||
|
self.move_to(self.cursor_x, self.cursor_y) |
||||||
|
|
||||||
|
def hal_backlight_on(self): |
||||||
|
"""Allows the hal layer to turn the backlight on. |
||||||
|
|
||||||
|
If desired, a derived HAL class will implement this function. |
||||||
|
""" |
||||||
|
pass |
||||||
|
|
||||||
|
def hal_backlight_off(self): |
||||||
|
"""Allows the hal layer to turn the backlight off. |
||||||
|
|
||||||
|
If desired, a derived HAL class will implement this function. |
||||||
|
""" |
||||||
|
pass |
||||||
|
|
||||||
|
def hal_write_command(self, cmd): |
||||||
|
"""Write a command to the LCD. |
||||||
|
|
||||||
|
It is expected that a derived HAL class will implement this |
||||||
|
function. |
||||||
|
""" |
||||||
|
raise NotImplementedError |
||||||
|
|
||||||
|
def hal_write_data(self, data): |
||||||
|
"""Write data to the LCD. |
||||||
|
|
||||||
|
It is expected that a derived HAL class will implement this |
||||||
|
function. |
||||||
|
""" |
||||||
|
raise NotImplementedError |
||||||
|
|
||||||
|
# This is a default implementation of hal_sleep_us which is suitable |
||||||
|
# for most micropython implementations. For platforms which don't |
||||||
|
# support `time.sleep_us()` they should provide their own implementation |
||||||
|
# of hal_sleep_us in their hal layer and it will be used instead. |
||||||
|
def hal_sleep_us(self, usecs): |
||||||
|
"""Sleep for some time (given in microseconds).""" |
||||||
|
time.sleep_us(usecs) # NOTE this is not part of Standard Python library, specific hal layers will need to override this |
@ -0,0 +1,80 @@ |
|||||||
|
|
||||||
|
from machine import ADC, I2C, Pin, PWM |
||||||
|
import time |
||||||
|
from pico_i2c_lcd import I2cLcd |
||||||
|
|
||||||
|
pressure_sensor = ADC(Pin(26, mode=Pin.IN)) |
||||||
|
hall_sensor = Pin(27, Pin.IN, Pin.PULL_UP) |
||||||
|
btnn_reset = Pin(22, Pin.IN) |
||||||
|
lcd = False |
||||||
|
buzzer_satus = False |
||||||
|
ii = 0 |
||||||
|
|
||||||
|
|
||||||
|
def interruption_handler(*args, **kargs): |
||||||
|
global ii |
||||||
|
ii += 1 |
||||||
|
|
||||||
|
hall_sensor.irq( |
||||||
|
handler=interruption_handler, |
||||||
|
trigger=Pin.IRQ_RISING, |
||||||
|
) |
||||||
|
previous_time = time.ticks_ms() |
||||||
|
|
||||||
|
|
||||||
|
def setup(): |
||||||
|
global pressure_sensor, lcd, buzzer |
||||||
|
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=100000) |
||||||
|
I2C_ADDR = i2c.scan()[0] |
||||||
|
lcd = I2cLcd(i2c, I2C_ADDR, 2, 16) |
||||||
|
buzzer = PWM(Pin(15)) |
||||||
|
buzzer.duty_u16(0) |
||||||
|
|
||||||
|
|
||||||
|
def get_rotation_frequency(): |
||||||
|
global ii, previous_time |
||||||
|
frequency = float(ii) / (time.ticks_ms() - previous_time) * 1e3 |
||||||
|
previous_time = time.ticks_ms() |
||||||
|
ii = 0 |
||||||
|
return frequency |
||||||
|
|
||||||
|
|
||||||
|
def get_pressure(): |
||||||
|
global pressure_sensor |
||||||
|
pressure_measure = float(pressure_sensor.read_u16()) / 65535 * 3.3 |
||||||
|
return pressure_measure |
||||||
|
|
||||||
|
|
||||||
|
def loop(): |
||||||
|
global lcd, buzzer, buzzer_satus |
||||||
|
pressure_measure = get_pressure() |
||||||
|
lcd.clear() |
||||||
|
str_pressure = "{:.3f}".format(pressure_measure) |
||||||
|
lcd.putstr('Preassure: ' + str_pressure + '\n') |
||||||
|
frequency = get_rotation_frequency() |
||||||
|
str_frequency = "{:.3f}".format(frequency) |
||||||
|
lcd.putstr('Frequency: ' + str_frequency) |
||||||
|
time.sleep(.07) |
||||||
|
buzzer_satus = not buzzer_satus |
||||||
|
if buzzer_satus: |
||||||
|
buzzer.duty_u16(220) |
||||||
|
else: |
||||||
|
buzzer.duty_u16(0) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_error(): |
||||||
|
global lcd |
||||||
|
lcd.putstr('Exception') |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
setup() |
||||||
|
try: |
||||||
|
while(True): |
||||||
|
loop() |
||||||
|
except: |
||||||
|
handle_error() |
||||||
|
while True: |
||||||
|
pass |
||||||
|
|
@ -0,0 +1,86 @@ |
|||||||
|
import utime |
||||||
|
import gc |
||||||
|
|
||||||
|
from lcd_api import LcdApi |
||||||
|
from machine import I2C |
||||||
|
|
||||||
|
# PCF8574 pin definitions |
||||||
|
MASK_RS = 0x01 # P0 |
||||||
|
MASK_RW = 0x02 # P1 |
||||||
|
MASK_E = 0x04 # P2 |
||||||
|
|
||||||
|
SHIFT_BACKLIGHT = 3 # P3 |
||||||
|
SHIFT_DATA = 4 # P4-P7 |
||||||
|
|
||||||
|
class I2cLcd(LcdApi): |
||||||
|
|
||||||
|
#Implements a HD44780 character LCD connected via PCF8574 on I2C |
||||||
|
|
||||||
|
def __init__(self, i2c, i2c_addr, num_lines, num_columns): |
||||||
|
self.i2c = i2c |
||||||
|
self.i2c_addr = i2c_addr |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([0])) |
||||||
|
utime.sleep_ms(20) # Allow LCD time to powerup |
||||||
|
# Send reset 3 times |
||||||
|
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET) |
||||||
|
utime.sleep_ms(5) # Need to delay at least 4.1 msec |
||||||
|
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET) |
||||||
|
utime.sleep_ms(1) |
||||||
|
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET) |
||||||
|
utime.sleep_ms(1) |
||||||
|
# Put LCD into 4-bit mode |
||||||
|
self.hal_write_init_nibble(self.LCD_FUNCTION) |
||||||
|
utime.sleep_ms(1) |
||||||
|
LcdApi.__init__(self, num_lines, num_columns) |
||||||
|
cmd = self.LCD_FUNCTION |
||||||
|
if num_lines > 1: |
||||||
|
cmd |= self.LCD_FUNCTION_2LINES |
||||||
|
self.hal_write_command(cmd) |
||||||
|
gc.collect() |
||||||
|
|
||||||
|
def hal_write_init_nibble(self, nibble): |
||||||
|
# Writes an initialization nibble to the LCD. |
||||||
|
# This particular function is only used during initialization. |
||||||
|
byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E])) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte])) |
||||||
|
gc.collect() |
||||||
|
|
||||||
|
def hal_backlight_on(self): |
||||||
|
# Allows the hal layer to turn the backlight on |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([1 << SHIFT_BACKLIGHT])) |
||||||
|
gc.collect() |
||||||
|
|
||||||
|
def hal_backlight_off(self): |
||||||
|
#Allows the hal layer to turn the backlight off |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([0])) |
||||||
|
gc.collect() |
||||||
|
|
||||||
|
def hal_write_command(self, cmd): |
||||||
|
# Write a command to the LCD. Data is latched on the falling edge of E. |
||||||
|
byte = ((self.backlight << SHIFT_BACKLIGHT) | |
||||||
|
(((cmd >> 4) & 0x0f) << SHIFT_DATA)) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E])) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte])) |
||||||
|
byte = ((self.backlight << SHIFT_BACKLIGHT) | |
||||||
|
((cmd & 0x0f) << SHIFT_DATA)) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E])) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte])) |
||||||
|
if cmd <= 3: |
||||||
|
# The home and clear commands require a worst case delay of 4.1 msec |
||||||
|
utime.sleep_ms(5) |
||||||
|
gc.collect() |
||||||
|
|
||||||
|
def hal_write_data(self, data): |
||||||
|
# Write data to the LCD. Data is latched on the falling edge of E. |
||||||
|
byte = (MASK_RS | |
||||||
|
(self.backlight << SHIFT_BACKLIGHT) | |
||||||
|
(((data >> 4) & 0x0f) << SHIFT_DATA)) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E])) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte])) |
||||||
|
byte = (MASK_RS | |
||||||
|
(self.backlight << SHIFT_BACKLIGHT) | |
||||||
|
((data & 0x0f) << SHIFT_DATA)) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E])) |
||||||
|
self.i2c.writeto(self.i2c_addr, bytes([byte])) |
||||||
|
gc.collect() |
Loading…
Reference in new issue