Advertisement
lightxx

Untitled

Aug 22nd, 2013
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 20.39 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. # Based on Adafruit Adafruit_CharLCDPlate.py
  4. # ==========================================
  5. # This script enable the MCP23017 Interruptions mode on port A
  6. # That means an interruption will be generated each time a button is pressed on Adafruit CharLCDPlate
  7. # Then if correctly hardwired to a Raspberry Pi GPIO,
  8. # this interrupt will in turn generates an interrupt on the Raspberry Pi itself,
  9. # allowing us to launch something...
  10. # With this method we dont have to constantly poll the buttons (we save CPU time)
  11. # ==========================================
  12. #                                           Guy / April 2013  / Version RPi.GPIO != RPIO
  13.  
  14. # Python library for Adafruit RGB-backlit LCD plate for Raspberry Pi.
  15. # Written by Adafruit Industries.  M.I.T. license.
  16.  
  17. # This is essentially a complete rewrite, but the calling syntax
  18. # and constants are based on code from lrvick and LiquidCrystal.
  19. # lrvic - https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py
  20. # LiquidCrystal - https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp
  21.  
  22. from Adafruit_I2C import Adafruit_I2C
  23. from time import sleep, strftime, time
  24. from sys import exit
  25.  
  26. import RPi.GPIO as GPIO
  27. #import os
  28.  
  29. class Adafruit_CharLCDPlate(Adafruit_I2C):
  30.  
  31.     # ----------------------------------------------------------------------
  32.     # Constants
  33.  
  34.     # Port expander registers
  35.     MCP23017_IOCON_BANK0    = 0x0A  # IOCON when Bank 0 active
  36.     MCP23017_IOCON_BANK1    = 0x15  # IOCON when Bank 1 active
  37.     # These are register addresses when in Bank 1 only:
  38.     MCP23017_GPIOA          = 0x09
  39.     MCP23017_IODIRB         = 0x10
  40.     MCP23017_GPIOB          = 0x19
  41.     MCP23017_GPINTENA       = 0x02
  42.     MCP23017_GPINTENB       = 0x12
  43.     MCP23017_DEFVALA        = 0x03
  44.     MCP23017_DEFVALB        = 0x13
  45.     MCP23017_INTCONA        = 0x04
  46.     MCP23017_INTCONB        = 0x14
  47.     MCP23017_INTCAPA        = 0x08
  48.     MCP23017_INTCAPB        = 0x18  
  49.  
  50.     # Port expander input pin definitions
  51.     SELECT                  = 0
  52.     RIGHT                   = 1
  53.     DOWN                    = 2
  54.     UP                      = 3
  55.     LEFT                    = 4
  56.  
  57.     # LED colors
  58.     # global OFF, RED, GREEN, BLUE, YELLOW, TEAL, VIOLET, WHITE, ON
  59.     OFF                     = 0x00
  60.     RED                     = 0x01
  61.     GREEN                   = 0x02
  62.     BLUE                    = 0x04
  63.     YELLOW                  = RED + GREEN
  64.     TEAL                    = GREEN + BLUE
  65.     VIOLET                  = RED + BLUE
  66.     WHITE                   = RED + GREEN + BLUE
  67.     ON                      = RED + GREEN + BLUE
  68.  
  69.     # LCD Commands
  70.     LCD_CLEARDISPLAY        = 0x01
  71.     LCD_RETURNHOME          = 0x02
  72.     LCD_ENTRYMODESET        = 0x04
  73.     LCD_DISPLAYCONTROL      = 0x08
  74.     LCD_CURSORSHIFT         = 0x10
  75.     LCD_FUNCTIONSET         = 0x20
  76.     LCD_SETCGRAMADDR        = 0x40
  77.     LCD_SETDDRAMADDR        = 0x80
  78.  
  79.     # Flags for display on/off control
  80.     LCD_DISPLAYON           = 0x04
  81.     LCD_DISPLAYOFF          = 0x00
  82.     LCD_CURSORON            = 0x02
  83.     LCD_CURSOROFF           = 0x00
  84.     LCD_BLINKON             = 0x01
  85.     LCD_BLINKOFF            = 0x00
  86.  
  87.     # Flags for display entry mode
  88.     LCD_ENTRYRIGHT          = 0x00
  89.     LCD_ENTRYLEFT           = 0x02
  90.     LCD_ENTRYSHIFTINCREMENT = 0x01
  91.     LCD_ENTRYSHIFTDECREMENT = 0x00
  92.  
  93.     # Flags for display/cursor shift
  94.     LCD_DISPLAYMOVE = 0x08
  95.     LCD_CURSORMOVE  = 0x00
  96.     LCD_MOVERIGHT   = 0x04
  97.     LCD_MOVELEFT    = 0x00
  98.  
  99.  
  100.     # ----------------------------------------------------------------------
  101.     # Constructor
  102.  
  103.     def __init__(self, busnum=-1, addr=0x20, debug=False):
  104.  
  105.         self.i2c = Adafruit_I2C(addr, busnum, debug)
  106.  
  107.         # I2C is relatively slow.  MCP output port states are cached
  108.         # so we don't need to constantly poll-and-change bit states.
  109.         self.porta, self.portb, self.ddrb = 0, 0, 0b00010000
  110.  
  111.         # Set MCP23017 IOCON register to Bank 0 with sequential operation.
  112.         # If chip is already set for Bank 0, this will just write to OLATB,
  113.         # which won't seriously bother anything on the plate right now
  114.         # (blue backlight LED will come on, but that's done in the next
  115.         # step anyway).
  116.         self.i2c.bus.write_byte_data(
  117.           self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
  118.  
  119.         # Brute force reload ALL registers to known state.  This also
  120.         # sets up all the input pins, pull-ups, etc. for the Pi Plate.
  121.         self.i2c.bus.write_i2c_block_data(
  122.           self.i2c.address, 0,
  123.           [ 0b00111111,   # IODIRA    R+G LEDs=outputs, buttons=inputs
  124.             self.ddrb ,   # IODIRB    LCD D7=input, Blue LED=output
  125.             0b00111111,   # IPOLA     Invert polarity on button inputs
  126.             0b00000000,   # IPOLB
  127.             0b00011111,   # GPINTENA  ENABLE interrupt-on-change MODIFICATION GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  128.             0b00000000,   # GPINTENB
  129.             0b00000000,   # DEFVALA                              MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  130.             0b00000000,   # DEFVALB
  131.             0b00000000,   # INTCONA                             MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  132.             0b00000000,   # INTCONB
  133.             0b00000000,   # IOCON                               MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  134.             0b00000000,   # IOCON                               MODIF GUY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  135.             0b00111111,   # GPPUA     Enable pull-ups on buttons
  136.             0b00000000,   # GPPUB
  137.             0b00000000,   # INTFA
  138.             0b00000000,   # INTFB
  139.             0b00000000,   # INTCAPA
  140.             0b00000000,   # INTCAPB
  141.             self.porta,   # GPIOA
  142.             self.portb,   # GPIOB
  143.             self.porta,   # OLATA     0 on all outputs; side effect of
  144.             self.portb ]) # OLATB     turning on R+G+B backlight LEDs.
  145.  
  146.         # Switch to Bank 1 and disable sequential operation.
  147.         # From this point forward, the register addresses do NOT match
  148.         # the list immediately above.  Instead, use the constants defined
  149.         # at the start of the class.  Also, the address register will no
  150.         # longer increment automatically after this -- multi-byte
  151.         # operations must be broken down into single-byte calls.
  152.         self.i2c.bus.write_byte_data(
  153.           self.i2c.address, self.MCP23017_IOCON_BANK0, 0b10100000)
  154.  
  155.         self.displayshift   = (self.LCD_CURSORMOVE |
  156.                                self.LCD_MOVERIGHT)
  157.         self.displaymode    = (self.LCD_ENTRYLEFT |
  158.                                self.LCD_ENTRYSHIFTDECREMENT)
  159.         self.displaycontrol = (self.LCD_DISPLAYON |
  160.                                self.LCD_CURSOROFF |
  161.                                self.LCD_BLINKOFF)
  162.  
  163.         self.write(0x33) # Init
  164.         self.write(0x32) # Init
  165.         self.write(0x28) # 2 line 5x8 matrix
  166.         self.write(self.LCD_CLEARDISPLAY)
  167.         self.write(self.LCD_CURSORSHIFT    | self.displayshift)
  168.         self.write(self.LCD_ENTRYMODESET   | self.displaymode)
  169.         self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
  170.         self.write(self.LCD_RETURNHOME)
  171.  
  172.  
  173.     # ----------------------------------------------------------------------
  174.     # Write operations
  175.  
  176.     # The LCD data pins (D4-D7) connect to MCP pins 12-9 (PORTB4-1), in
  177.     # that order.  Because this sequence is 'reversed,' a direct shift
  178.     # won't work.  This table remaps 4-bit data values to MCP PORTB
  179.     # outputs, incorporating both the reverse and shift.
  180.     flip = ( 0b00000000, 0b00010000, 0b00001000, 0b00011000,
  181.              0b00000100, 0b00010100, 0b00001100, 0b00011100,
  182.              0b00000010, 0b00010010, 0b00001010, 0b00011010,
  183.              0b00000110, 0b00010110, 0b00001110, 0b00011110 )
  184.  
  185.     # Low-level 4-bit interface for LCD output.  This doesn't actually
  186.     # write data, just returns a byte array of the PORTB state over time.
  187.     # Can concatenate the output of multiple calls (up to 8) for more
  188.     # efficient batch write.
  189.     def out4(self, bitmask, value):
  190.         hi = bitmask | self.flip[value >> 4]
  191.         lo = bitmask | self.flip[value & 0x0F]
  192.         return [hi | 0b00100000, hi, lo | 0b00100000, lo]
  193.  
  194.  
  195.     # The speed of LCD accesses is inherently limited by I2C through the
  196.     # port expander.  A 'well behaved program' is expected to poll the
  197.     # LCD to know that a prior instruction completed.  But the timing of
  198.     # most instructions is a known uniform 37 mS.  The enable strobe
  199.     # can't even be twiddled that fast through I2C, so it's a safe bet
  200.     # with these instructions to not waste time polling (which requires
  201.     # several I2C transfers for reconfiguring the port direction).
  202.     # The D7 pin is set as input when a potentially time-consuming
  203.     # instruction has been issued (e.g. screen clear), as well as on
  204.     # startup, and polling will then occur before more commands or data
  205.     # are issued.
  206.  
  207.     pollables = ( LCD_CLEARDISPLAY, LCD_RETURNHOME )
  208.  
  209.     # Write byte, list or string value to LCD
  210.     def write(self, value, char_mode=False):
  211.         """ Send command/data to LCD """
  212.  
  213.         # If pin D7 is in input state, poll LCD busy flag until clear.
  214.         if self.ddrb & 0b00010000:
  215.             lo = (self.portb & 0b00000001) | 0b01000000
  216.             hi = lo | 0b00100000 # E=1 (strobe)
  217.             self.i2c.bus.write_byte_data(
  218.               self.i2c.address, self.MCP23017_GPIOB, lo)
  219.             while True:
  220.                 # Strobe high (enable)
  221.                 self.i2c.bus.write_byte(self.i2c.address, hi)
  222.                 # First nybble contains busy state
  223.                 bits = self.i2c.bus.read_byte(self.i2c.address)
  224.                 # Strobe low, high, low.  Second nybble (A3) is ignored.
  225.                 self.i2c.bus.write_i2c_block_data(
  226.                   self.i2c.address, self.MCP23017_GPIOB, [lo, hi, lo])
  227.                 if (bits & 0b00000010) == 0: break # D7=0, not busy
  228.             self.portb = lo
  229.  
  230.             # Polling complete, change D7 pin to output
  231.             self.ddrb &= 0b11101111
  232.             self.i2c.bus.write_byte_data(self.i2c.address,
  233.               self.MCP23017_IODIRB, self.ddrb)
  234.  
  235.         bitmask = self.portb & 0b00000001   # Mask out PORTB LCD control bits
  236.         if char_mode: bitmask |= 0b10000000 # Set data bit if not a command
  237.  
  238.         # If string or list, iterate through multiple write ops
  239.         if isinstance(value, str):
  240.             last = len(value) - 1 # Last character in string
  241.             data = []             # Start with blank list
  242.             for i, v in enumerate(value): # For each character...
  243.                 # Append 4 bytes to list representing PORTB over time.
  244.                 # First the high 4 data bits with strobe (enable) set
  245.                 # and unset, then same with low 4 data bits (strobe 1/0).
  246.                 data.extend(self.out4(bitmask, ord(v)))
  247.                 # I2C block data write is limited to 32 bytes max.
  248.                 # If limit reached, write data so far and clear.
  249.                 # Also do this on last byte if not otherwise handled.
  250.                 if (len(data) >= 32) or (i == last):
  251.                     self.i2c.bus.write_i2c_block_data(
  252.                       self.i2c.address, self.MCP23017_GPIOB, data)
  253.                     self.portb = data[-1] # Save state of last byte out
  254.                     data       = []       # Clear list for next iteration
  255.         elif isinstance(value, list):
  256.             # Same as above, but for list instead of string
  257.             last = len(value) - 1
  258.             data = []
  259.             for i, v in enumerate(value):
  260.                 data.extend(self.out4(bitmask, v))
  261.                 if (len(data) >= 32) or (i == last):
  262.                     self.i2c.bus.write_i2c_block_data(
  263.                       self.i2c.address, self.MCP23017_GPIOB, data)
  264.                     self.portb = data[-1]
  265.                     data       = []
  266.         else:
  267.             # Single byte
  268.             data = self.out4(bitmask, value)
  269.             self.i2c.bus.write_i2c_block_data(
  270.               self.i2c.address, self.MCP23017_GPIOB, data)
  271.             self.portb = data[-1]
  272.  
  273.         # If a poll-worthy instruction was issued, reconfigure D7
  274.         # pin as input to indicate need for polling on next call.
  275.         if (not char_mode) and (value in self.pollables):
  276.             self.ddrb |= 0b00010000
  277.             self.i2c.bus.write_byte_data(self.i2c.address,
  278.               self.MCP23017_IODIRB, self.ddrb)
  279.  
  280.  
  281.     # ----------------------------------------------------------------------
  282.     # Utility methods
  283.  
  284.     def begin(self, cols, lines):
  285.         self.currline = 0
  286.         self.numlines = lines
  287.         self.clear()
  288.  
  289.  
  290.     # Puts the MCP23017 back in Bank 0 + sequential write mode so
  291.     # that other code using the 'classic' library can still work.
  292.     # Any code using this newer version of the library should
  293.     # consider adding an atexit() handler that calls this.
  294.     def stop(self):
  295.         self.porta = 0b11000000  # Turn off LEDs on the way out
  296.         self.portb = 0b00000001
  297.         sleep(0.0015)
  298.         self.i2c.bus.write_byte_data(
  299.           self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
  300.         self.i2c.bus.write_i2c_block_data(
  301.           self.i2c.address, 0,
  302.           [ 0b00111111,   # IODIRA
  303.             self.ddrb ,   # IODIRB
  304.             0b00000000,   # IPOLA
  305.             0b00000000,   # IPOLB
  306.             0b00000000,   # GPINTENA
  307.             0b00000000,   # GPINTENB
  308.             0b00000000,   # DEFVALA
  309.             0b00000000,   # DEFVALB
  310.             0b00000000,   # INTCONA
  311.             0b00000000,   # INTCONB
  312.             0b00000000,   # IOCON
  313.             0b00000000,   # IOCON
  314.             0b00111111,   # GPPUA
  315.             0b00000000,   # GPPUB
  316.             0b00000000,   # INTFA
  317.             0b00000000,   # INTFB
  318.             0b00000000,   # INTCAPA
  319.             0b00000000,   # INTCAPB
  320.             self.porta,   # GPIOA
  321.             self.portb,   # GPIOB
  322.             self.porta,   # OLATA
  323.             self.portb ]) # OLATB
  324.  
  325.  
  326.     def clear(self):
  327.         self.write(self.LCD_CLEARDISPLAY)
  328.  
  329.     def message(self, text):
  330.         """ Send string to LCD. Newline wraps to second line"""
  331.         lines = text.split('\n')   # Split at newline(s)
  332.         for line in lines:         # Render each substring...
  333.             self.write(line, True)
  334.             if len(lines) > 1:     # If newline(s),
  335.                 self.write(0xC0)   # set DDRAM address to second line
  336.  
  337.     def backlight(self, color):
  338.         c          = ~color
  339.         self.porta = (self.porta & 0b00111111) | ((c & 0b011) << 6)
  340.         self.portb = (self.portb & 0b11111110) | ((c & 0b100) >> 2)
  341.         # Has to be done as two writes because sequential operation is off.
  342.         self.i2c.bus.write_byte_data(
  343.           self.i2c.address, self.MCP23017_GPIOA, self.porta)
  344.         self.i2c.bus.write_byte_data(
  345.           self.i2c.address, self.MCP23017_GPIOB, self.portb)
  346.  
  347.     def buttonPressed(self, b):
  348.         return (self.i2c.readU8(self.MCP23017_GPIOA) >> b) & 1
  349.  
  350.     def ReadGPINTENA(self):
  351.         return (self.i2c.readU8(self.MCP23017_GPINTENA))
  352.  
  353.     def ReadDEFVALA(self):
  354.         return (self.i2c.readU8(self.MCP23017_DEFVALA))
  355.        
  356.     def ReadINTCONA(self):
  357.         return (self.i2c.readU8(self.MCP23017_INTCONA))
  358.  
  359.     def ReadINTCAPA(self):
  360.         return (self.i2c.readU8(self.MCP23017_INTCAPA) & 0b00011111)
  361.            
  362.     def gpio_callback(self, channel):
  363.         # print("gpio %s: %s" % (gpio_id, val))
  364.         sleep(0.3) # wait button release Not very good to try by reading GPIO # not needed since debounce # debounce doesn't give expected results
  365.         global BUTTON
  366.         BUTTON = lcd.ReadINTCAPA()
  367.        
  368.     def RunMenu(self):
  369.         global STATE
  370.         if STATE == 1:   # Display menu
  371.             TEXT = m[MENU] + "\nSELECT   UP/DOWN"
  372.             # print TEXT
  373.             lcd.clear()
  374.             lcd.message(TEXT)  
  375.         elif STATE == 2:   # Select has been pressed, a menu item choosen
  376.             if MENU == 0: # cpu temp
  377.                 f = open('/sys/class/thermal/thermal_zone0/temp', 'r')
  378.                 TEMP = f.read()
  379.                 LIST = list(TEMP)
  380.                 LIST.insert(2, '.')
  381.                 TEMP = "".join(LIST)
  382.                 TEXT = "Temp CPU:" + TEMP # + "\nBUTTON = " + str(BUTTON)
  383.                 lcd.clear()
  384.                 lcd.message(TEXT)
  385.             elif MENU == 1: # gpu temp
  386.                 TEXT = "Not available :("
  387.                 lcd.clear()
  388.                 lcd.message(TEXT)
  389.             elif MENU == 2: # cpu frequency
  390.                 f = open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', 'r')
  391.                 FREQ = f.read()
  392.                 TEXT = "Freq.=" + FREQ
  393.                 lcd.clear()
  394.                 lcd.message(TEXT)
  395.             elif MENU == 3:  # time
  396.                 TEXT = strftime('%H:%M %d-%b-%y')
  397.                 lcd.clear()
  398.                 lcd.message(TEXT)
  399.             elif MENU == 4: # exit
  400.                 lcd.clear()
  401.                 lcd.backlight(lcd.OFF)
  402.                 GPIO.remove_event_detect(ENTRY)
  403.                 exit()
  404.         STATE = 0
  405.  
  406.     # ----------------------------------------------------------------------
  407.     # Test code
  408.  
  409. if __name__ == '__main__':
  410.    
  411.     lcd = Adafruit_CharLCDPlate()
  412.     col = (lcd.RED, lcd.YELLOW, lcd.GREEN, lcd.TEAL, lcd.BLUE, lcd.VIOLET, lcd.OFF, lcd.ON)
  413.     m = ("Temp CPU", "Temp GPU", "Frequecy", "Time", "Exit program")
  414.     COLOR = 6 # COLOR varies 0 to 7 starts with off
  415.     STATE = 0
  416.     BUTTON = 0
  417.     MENU = 0
  418.     FirstCall = True
  419.     TimeMax = 30 # lcd off if no button pressed after TimeMax seconds
  420.    
  421.     GPIO.setmode(GPIO.BCM)
  422.     GPIO.setwarnings(False)
  423.     ENTRY = 23
  424.     GPIO.setup(ENTRY, GPIO.IN)
  425.  
  426.     lcd.begin(16, 2)
  427.     lcd.clear()
  428.     lcd.message("Initialization\n......")
  429.     sleep(3)
  430.  
  431.     lcd.ReadINTCAPA() # Reset MCP23017 Interrupt by reading INTCAPA then we are ready to generate another Interrupt
  432.  
  433.     lcd.backlight(lcd.OFF)
  434.     lcd.clear()
  435.     lcd.message("Init done\nInterrupt enable")
  436.  
  437.     # GPIO.wait_for_interrupts(threaded=True)
  438.     # GPIO.add_interrupt_callback(23, lcd.gpio_callback, edge='falling', threaded_callback=True, debounce_timeout_ms=150)
  439.     # with debounce sometimes the progran freeze... Back to sleep(0.3) in gpio_callback
  440.     # GPIO.add_interrupt_callback(23, lcd.gpio_callback, edge='falling', threaded_callback=True)
  441.     GPIO.add_event_detect(ENTRY, GPIO.FALLING, lcd.gpio_callback)
  442.    
  443.     while True:
  444.         if BUTTON != 0:  # A button has been pressed?
  445.             # We go through this loop once only each time a BUTTON is pressed
  446.             StartTime = time()
  447.             if FirstCall == True:
  448.                 FirstCall = False
  449.                 lcd.clear()
  450.                 lcd.message("LEFT/RIGHT=COLOR\nUP/DOWN= MENU")
  451.             else:
  452.                 if BUTTON == 1:
  453.                     STATE = 2
  454.                     lcd.RunMenu()
  455.                 elif BUTTON == 8: # Menu UP
  456.                     MENU = MENU + 1
  457.                     if MENU > 4: # Menu varies 0 to 4
  458.                         MENU = 0
  459.                     STATE = 1
  460.                     lcd.RunMenu()
  461.                 elif BUTTON == 4: # Menu DOWN
  462.                     MENU = MENU - 1
  463.                     if MENU < 0: # Menu varies 0 to 4
  464.                         MENU = 4
  465.                     STATE = 1
  466.                     lcd.RunMenu()
  467.                 elif BUTTON == 16: # Color down
  468.                     COLOR = COLOR - 1
  469.                     if COLOR < 0:
  470.                         COLOR = 7
  471.                     lcd.backlight(col[COLOR])
  472.                 elif BUTTON == 2: # Color up
  473.                     COLOR = COLOR + 1
  474.                     if COLOR > 7:
  475.                         COLOR = 0
  476.                     lcd.backlight(col[COLOR])
  477.             BUTTON = 0        # BUTTON will be set by gpio_callback if an interrupt occurs i.e. a button is pressed
  478.         if FirstCall == False:
  479.             elapsed = (time() - StartTime)
  480.             if elapsed > TimeMax:
  481.                 lcd.clear()
  482.                 lcd.backlight(lcd.OFF)
  483.                 FirstCall = True
  484.         sleep(0.1) # Without this python goes in the loop so fast that it uses all CPU ressources: top reports python uses 98% of CPU.
  485.         #            I don't like that. And we use interrupt to minimize the use of cpu!.... ;-)
  486.     print "I never come HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement