<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hobby &#187; Electronics</title>
	<atom:link href="https://hobby.farit.ru/category/electronics/feed/" rel="self" type="application/rss+xml" />
	<link>https://hobby.farit.ru</link>
	<description></description>
	<lastBuildDate>Fri, 27 Feb 2026 07:17:33 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.39</generator>
	<item>
		<title>HML087 coding plug ROM for BMW E30 instrument cluster</title>
		<link>https://hobby.farit.ru/hml087-coder-rom-for-e30-instrument-cluster/</link>
		<comments>https://hobby.farit.ru/hml087-coder-rom-for-e30-instrument-cluster/#comments</comments>
		<pubDate>Sat, 15 Feb 2025 21:39:47 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=693</guid>
		<description><![CDATA[The E30 instrument cluster for newer models have a coding plug (codier stecker) in front that stores the configuration parameters for SI boards: 6 or 4 cylinders, the maximum RPM, etc. If you open the coding plug, you can find the chip HML087. It&#8217;s a 40-year-old ROM that uses its own communication protocol. The coding [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>The E30 instrument cluster for newer models have a coding plug (codier stecker) in front that stores the configuration parameters for SI boards: 6 or 4 cylinders, the maximum RPM, etc.<br />
If you open the coding plug, you can find the chip HML087. It&#8217;s a 40-year-old ROM that uses its own communication protocol.</p>
<h3>The coding plug pins</h3>
<table>
<tr>
<th>Pin</th>
<th>Function</th>
<th>Description</th>
</tr>
<tr>
<td>1</td>
<td>CLOCK</td>
<td>The data is sent on its level going from high to low</td>
</tr>
<tr>
<td>2</td>
<td>GND</td>
<td>Ground</td>
</tr>
<tr>
<td>3</td>
<td>+5V</td>
<td>Power to the chip</td>
</tr>
<tr>
<td>4</td>
<td>GND</td>
<td>Ground</td>
</tr>
<tr>
<td>5</td>
<td>DATA</td>
<td>The output data: high &#8211; 1, low &#8211; 0</td>
</tr>
<tr>
<td>6</td>
<td>GND</td>
<td>Ground</td>
</tr>
<tr>
<td>7</td>
<td>CS</td>
<td>The ROM is selected when the CS level is high</td>
</tr>
<tr>
<td>8</td>
<td>GND</td>
<td>Ground</td>
</tr>
</table>
<h3>Communication protocol capture</h3>
<p>The protocol can be captured using a Saleae compatible logic analyzer and the Saleae software. </p>
<p>The coding plug for HML087 1385468 (E30 325i M20B25 7000RPM USA)<br />
<a href="http://hobby.farit.ru/wp-content/uploads/2025/02/hml087_1385468.png"><img src="http://hobby.farit.ru/wp-content/uploads/2025/02/hml087_1385468-854x172.png" alt="hml087_1385468" width="854" height="172" class="aligncenter size-medium wp-image-697" /></a></p>
<p>The communication protocol description.<br />
The pin CS is set high by a microprocessor. Then, the microprocessor sends 64 CLOCK signals. When the CLOCK signal goes from high to low, the ROM outputs data on the DATA pin.</p>
<h3>Data from different E30 coding plugs</h3>
<table>
<tr>
<th>Coding plug</th>
<th>Description</th>
<th>0</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>9</th>
<th>10</th>
<th>11</th>
<th>12</th>
<th>13</th>
<th>14</th>
<th>15</th>
</tr>
<tr>
<td>1385468</td>
<td>E30 325i M20B25 7000RPM USA</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1380873</td>
<td>E30 M3 S14 8000RPM Europe</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1377368</td>
<td>E30 325e M20B27 5000RPM USA</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>1394321</td>
<td>E30 318is M42B18 7000RPM USA</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1385364</td>
<td>E30 318i M40B18 7000RPM Europe</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>16</th>
<th>17</th>
<th>18</th>
<th>19</th>
<th>20</th>
<th>21</th>
<th>22</th>
<th>23</th>
<th>24</th>
<th>25</th>
<th>26</th>
<th>27</th>
<th>28</th>
<th>29</th>
<th>30</th>
<th>31</th>
</tr>
<tr>
<td>1385468</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1380873</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1377368</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1394321</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1385364</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>32</th>
<th>33</th>
<th>34</th>
<th>35</th>
<th>36</th>
<th>37</th>
<th>38</th>
<th>39</th>
<th>40</th>
<th>41</th>
<th>42</th>
<th>43</th>
<th>44</th>
<th>45</th>
<th>46</th>
<th>47</th>
</tr>
<tr>
<td>1385468</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>1380873</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1377368</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>1394321</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>1385364</td>
<td>&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>48</th>
<th>49</th>
<th>50</th>
<th>51</th>
<th>52</th>
<th>53</th>
<th>54</th>
<th>55</th>
<th>56</th>
<th>57</th>
<th>58</th>
<th>59</th>
<th>60</th>
<th>61</th>
<th>62</th>
<th>63</th>
</tr>
<tr>
<td>1385468</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>1380873</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>1377368</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>1394321</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>1385364</td>
<td>&nbsp;</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
</table>
<h3>HML087 emulation</h3>
<p>The communication protocol can be emulated by a microprocessor. While the Attiny family looks like an obvious choice, it&#8217;s too slow for the application. It will work with older SI boards where the CLOCK signal period is 30 µseconds, but in the newer SI boards the period is 5 µseconds.</p>
<h3>HML087 emulator that uses the microprocessor ch32v003</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2025/02/hml087_emulator.png"><img src="http://hobby.farit.ru/wp-content/uploads/2025/02/hml087_emulator-854x535.png" alt="hml087_emulator" width="854" height="535" class="aligncenter size-medium wp-image-717" /></a></p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2025/02/hml087_emulator_schema.png"><img src="http://hobby.farit.ru/wp-content/uploads/2025/02/hml087_emulator_schema-854x519.png" alt="hml087_emulator_schema" width="854" height="519" class="aligncenter size-medium wp-image-719" /></a></p>
<p>Here is the software for the BMW E30 coding plug emulator that uses the RISC-V microcontroller CH32V003: <a href="https://github.com/faritka/hml087" rel="noopener" target="_blank">https://github.com/faritka/hml087</a></p>
<p>You can check the timing diagram for the coder.</p>
<p>The CLOCK signal pulse period is 5.583 µs as the programmed coder was inserted into one of the latest SI boards.<br />
When the CLOCK signal changes from HIGH to LOW, the coder reacts after 725 ns (0.725 µs) and sends 0 (LOW) on the DATA line. This delay is acceptable for the latest SI boards. </p>
<p>The CLOCK signal pulse period is about 30 µs for earlier SI boards, and the coder certainly works with them.</p>
<p><a href="https://hobby.farit.ru/wp-content/uploads/2025/02/coder_diagram.jpg"><img src="https://hobby.farit.ru/wp-content/uploads/2025/02/coder_diagram-816x854.jpg" alt="coder_diagram" width="816" height="854" class="aligncenter size-medium wp-image-752" /></a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/hml087-coder-rom-for-e30-instrument-cluster/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>6-button digital clock for BMW E30</title>
		<link>https://hobby.farit.ru/bmw-e30-clock-zephyr-stm32/</link>
		<comments>https://hobby.farit.ru/bmw-e30-clock-zephyr-stm32/#comments</comments>
		<pubDate>Tue, 15 Aug 2023 05:14:18 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=657</guid>
		<description><![CDATA[The clock is engineered to be installed into the existing 6-button BMW E30 digital clock enclosure. The digits and letters are drawn by five red LTP-305HR 5&#215;7 dot matrix displays covered by an orange filter. There are two PCBs: the first is for the power supply and processor, the second is for the displays, buttons, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>The clock is engineered to be installed into the existing 6-button BMW E30 digital clock enclosure. The digits and letters are drawn by five red LTP-305HR 5&#215;7 dot matrix displays covered by an orange filter. There are two PCBs: the first is for the power supply and processor, the second is for the displays,  buttons, and display chip. The time functions are processed by the RTC module RV-3032-C7, which is temperature-compensated and very accurate. When the power is off, the RTC module gets power from the supercapacitor, which can supply enough power for a few months. The light sensor VEML7700 provides information about the current light level. The display dims at night. The optocoupler provides isolation from other car electronics.</p>
<p>The software is written using RTOS Zephyr for the processor STM32L452RE. There are several threads that read and update the current time, check the light level and adjust the display brightness, process interrupts fired by controlling buttons. A watchdog process restarts the processor in case of a serious error.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/clock_parts.jpg"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/clock_parts-854x423.jpg" alt="clock_parts" width="854" height="423" class="aligncenter size-medium wp-image-664" /></a></p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/clock_open.jpg"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/clock_open-854x569.jpg" alt="clock_open" width="854" height="569" class="aligncenter size-medium wp-image-666" /></a></p>
<h3>The processor is programmed by connecting it to an STM32 Nucleo board.</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/clock_programming.jpg"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/clock_programming-854x639.jpg" alt="clock programming" width="854" height="639" class="aligncenter size-medium wp-image-667" /></a></p>
<h3>The display PCB.</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/display.png"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/display-854x604.png" alt="Display" width="854" height="604" class="aligncenter size-medium wp-image-658" /></a></p>
<h3>The processor PCB.</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/processor.png"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/processor-854x604.png" alt="processor" width="854" height="604" class="aligncenter size-medium wp-image-659" /></a></p>
<h3>The power module.</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/power.png"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/power-854x604.png" alt="power" width="854" height="604" class="aligncenter size-medium wp-image-660" /></a></p>
<h3>The supercapacitor charger module.</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2023/08/battery_charger.png"><img src="http://hobby.farit.ru/wp-content/uploads/2023/08/battery_charger-854x604.png" alt="battery_charger" width="854" height="604" class="aligncenter size-medium wp-image-661" /></a></p>
<p>Software: <a href="https://github.com/faritka/e30clock" target="_blank">BMW E30 clock software for Zephyr RTOS and STM32</a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/bmw-e30-clock-zephyr-stm32/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>LED Matrix Font for HT1632C and Zephyr RTOS</title>
		<link>https://hobby.farit.ru/led-matrix-font-ht1632c-zephyr-rtos/</link>
		<comments>https://hobby.farit.ru/led-matrix-font-ht1632c-zephyr-rtos/#comments</comments>
		<pubDate>Fri, 30 Jul 2021 19:18:33 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[ht1632c]]></category>
		<category><![CDATA[stm32]]></category>
		<category><![CDATA[zephyr]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=629</guid>
		<description><![CDATA[Now, when the HT1632C display driver for Zephyr RTOS is ready, we want to write characters into the display buffer and send the buffer to the LED matrix. LED matrix The LED indicator is the LTP-305HR 5&#215;7 dot matrix display. It has an LED for a dot ., that is located at the column 0. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Now, when the <a href="http://hobby.farit.ru/ht1632-driver-rtos-zephyr-nucleo-stm32/" rel="noopener" target="_blank">HT1632C display driver for Zephyr RTOS</a> is ready, we want to write characters into the display buffer and send the buffer to the LED matrix.</p>
<h3>LED matrix</h3>
<p>The LED indicator is the LTP-305HR 5&#215;7 dot matrix display. It has an LED for a dot ., that is located at the column 0. So, we will start writing characters at the column 1 of the display buffer.</p>
<p>The HT1632C supports 32 ROWs &#8211; in our case they will be vertical columns, and 8 COMs &#8211; they will be horizontal rows. </p>
<p>The center of coordinates 0x0 starts at the left top.</p>
<p>In our case, we will use 5&#215;7 fixed width fonts. Each character will be represented as an array of 5 uint8_t 8-bit numbers(bytes). The array will start from the leftest column. The top row will start from the most significant(the leftest) bit of a byte.</p>
<p>Here is the letter <strong>M</strong> displayed on the LED matrix. When a bit is set to 1, it turns on the LED at that position; when it is set to 0, it turns off the LED. The 8th bit doesn&#8217;t matter as it&#8217;s not displayed at all &#8211; we will set it to 0.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/07/led_m.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/07/led_m.png" alt="led matrix m" width="500" height="468" class="aligncenter size-full wp-image-633" /></a></p>
<p>The C array for the letter M looks like this:<br />
{Column 1, Column 2, Column 3, Column 4, Column 5}</p>
<pre>{0b11111110, 0b01000000, 0b00110000, 0b01000000, 0b11111110}, // M</pre>
<h3>Font File</h3>
<p>All characters for the 5&#215;7 font are included in the file font_5x7.h</p>
<p>They are located according to the ASCII order starting from the character SPACE &#8216; &#8216;, which has the decimal number 32.</p>
<pre>
#ifndef FONT_5x7_H
#define FONT_5x7_H

// The width of each font character
#define APP_FONT_WIDTH 5

const uint8_t font[][APP_FONT_WIDTH] = {
    {0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // 
    {0b00000000, 0b00000000, 0b11111010, 0b00000000, 0b00000000}, // !
    {0b00000000, 0b11100000, 0b00000000, 0b11100000, 0b00000000}, // "
    {0b00101000, 0b11111110, 0b00101000, 0b11111110, 0b00101000}, // #
    {0b00101000, 0b01010100, 0b11111110, 0b01010100, 0b00101000}, // $
    {0b11000100, 0b11001000, 0b00010000, 0b00100110, 0b01000110}, // %
    {0b01101100, 0b10010010, 0b10101010, 0b01000100, 0b00001010}, // &#038;
    {0b00000000, 0b00000000, 0b11100000, 0b00000000, 0b00000000}, // '
    {0b00000000, 0b00111000, 0b01000100, 0b10000010, 0b00000000}, // (
    {0b00000000, 0b10000010, 0b01000100, 0b00111000, 0b00000000}, // )
    {0b00101000, 0b00010000, 0b01111100, 0b00010000, 0b00101000}, // *
    {0b00010000, 0b00010000, 0b01111100, 0b00010000, 0b00010000}, // +
    {0b00000000, 0b00001010, 0b00001100, 0b00000000, 0b00000000}, // ,
    {0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00010000}, // -
    {0b00000000, 0b00000110, 0b00000110, 0b00000000, 0b00000000}, // .
    {0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000}, // /
    {0b01111100, 0b10000010, 0b10000010, 0b10000010, 0b01111100}, // 0
    {0b00000000, 0b01000010, 0b11111110, 0b00000010, 0b00000000}, // 1
    {0b01000010, 0b10000110, 0b10001010, 0b10010010, 0b01100010}, // 2
    {0b01000100, 0b10000010, 0b10010010, 0b10010010, 0b01101100}, // 3
    {0b00001000, 0b00011000, 0b00101000, 0b01001000, 0b11111110}, // 4
    {0b11110100, 0b10010010, 0b10010010, 0b10010010, 0b10001100}, // 5
    {0b01111100, 0b10010010, 0b10010010, 0b10010010, 0b01001100}, // 6
    {0b10000000, 0b10001110, 0b10010000, 0b10100000, 0b11000000}, // 7
    {0b01101100, 0b10010010, 0b10010010, 0b10010010, 0b01101100}, // 8
    {0b01100100, 0b10010010, 0b10010010, 0b10010010, 0b01111100}, // 9
    {0b00000000, 0b01101100, 0b01101100, 0b00000000, 0b00000000}, // :
    {0b00000000, 0b01101010, 0b01101100, 0b00000000, 0b00000000}, // ;
    {0b00010000, 0b00101000, 0b01000100, 0b10000010, 0b00000000}, // <
    {0b00101000, 0b00101000, 0b00101000, 0b00101000, 0b00101000}, // =
    {0b10000010, 0b01000100, 0b00101000, 0b00010000, 0b00000000}, // >
    {0b01000000, 0b10000000, 0b10001010, 0b10010000, 0b01100000}, // ?
    {0b01111100, 0b10000010, 0b10011000, 0b10100100, 0b01111000}, // @
    {0b01111110, 0b10010000, 0b10010000, 0b10010000, 0b01111110}, // A
    {0b11111110, 0b10010010, 0b10010010, 0b10010010, 0b01101100}, // B
    {0b01111100, 0b10000010, 0b10000010, 0b10000010, 0b01000100}, // C
    {0b11111110, 0b10000010, 0b10000010, 0b10000010, 0b01111100}, // D
    {0b11111110, 0b10010010, 0b10010010, 0b10010010, 0b10000010}, // E
    {0b11111110, 0b10010000, 0b10010000, 0b10010000, 0b10000000}, // F
    {0b01111100, 0b10000010, 0b10010010, 0b10010010, 0b01011110}, // G
    {0b11111110, 0b00010000, 0b00010000, 0b00010000, 0b11111110}, // H
    {0b00000000, 0b10000010, 0b11111110, 0b10000010, 0b00000000}, // I
    {0b00000100, 0b10000010, 0b10000010, 0b10000010, 0b11111100}, // J
    {0b11111110, 0b00010000, 0b00101000, 0b01000100, 0b10000010}, // K
    {0b11111110, 0b00000010, 0b00000010, 0b00000010, 0b00000010}, // L
    {0b11111110, 0b01000000, 0b00110000, 0b01000000, 0b11111110}, // M
    {0b11111110, 0b00100000, 0b00010000, 0b00001000, 0b11111110}, // N
    {0b01111100, 0b10000010, 0b10000010, 0b10000010, 0b01111100}, // O
    {0b11111110, 0b10010000, 0b10010000, 0b10010000, 0b01100000}, // P
    {0b01111100, 0b10000010, 0b10001010, 0b10000100, 0b01111010}, // Q
    {0b11111110, 0b10010000, 0b10011000, 0b10010100, 0b01100010}, // R
    {0b01100100, 0b10010010, 0b10010010, 0b10010010, 0b01001100}, // S
    {0b10000000, 0b10000000, 0b11111110, 0b10000000, 0b10000000}, // T
    {0b11111100, 0b00000010, 0b00000010, 0b00000010, 0b11111100}, // U
    {0b11111000, 0b00000100, 0b00000010, 0b00000100, 0b11111000}, // V
    {0b11111100, 0b00000010, 0b00111100, 0b00000010, 0b11111100}, // W
    {0b11000110, 0b00101000, 0b00010000, 0b00101000, 0b11000110}, // X
    {0b11100000, 0b00010000, 0b00001110, 0b00010000, 0b11100000}, // Y
    {0b10000110, 0b10001010, 0b10010010, 0b10100010, 0b11000010}, // Z
    {0b00000000, 0b11111110, 0b10000010, 0b00000000, 0b00000000}, // [
    {0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100}, /* \ */
    {0b00000000, 0b10000010, 0b11111110, 0b00000000, 0b00000000}, // ]
    {0b00100000, 0b01000000, 0b10000000, 0b01000000, 0b00100000}, // ^
    {0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010}, // _
    {0b00000000, 0b10000000, 0b01000000, 0b00000000, 0b00000000}, // `
    {0b00000100, 0b00101010, 0b00101010, 0b00101010, 0b00011110}, // a
    {0b11111110, 0b00010010, 0b00100010, 0b00100010, 0b00011100}, // b
    {0b00011100, 0b00100010, 0b00100010, 0b00100010, 0b00000100}, // c
    {0b00011100, 0b00100010, 0b00100010, 0b00010010, 0b11111110}, // d
    {0b00011100, 0b00101010, 0b00101010, 0b00101010, 0b00011000}, // e
    {0b00000000, 0b00010000, 0b01111110, 0b10010000, 0b01000000}, // f
    {0b00010000, 0b00101010, 0b00101010, 0b00101010, 0b00111100}, // g
    {0b11111110, 0b00010000, 0b00100000, 0b00100000, 0b00011110}, // h
    {0b00000000, 0b00100010, 0b10111110, 0b00000010, 0b00000000}, // i
    {0b00000100, 0b00000010, 0b00100010, 0b10111100, 0b00000000}, // j
    {0b11111110, 0b00001000, 0b00010100, 0b00100010, 0b00000000}, // k
    {0b00000000, 0b10000010, 0b11111110, 0b00000010, 0b00000000}, // l
    {0b00111110, 0b00100000, 0b00011110, 0b00100000, 0b00011110}, // m
    {0b00111110, 0b00010000, 0b00100000, 0b00100000, 0b00011110}, // n
    {0b00011100, 0b00100010, 0b00100010, 0b00100010, 0b00011100}, // o
    {0b00111110, 0b00101000, 0b00101000, 0b00101000, 0b00010000}, // p
    {0b00010000, 0b00101000, 0b00101000, 0b00101000, 0b00111110}, // q
    {0b00111110, 0b00010000, 0b00100000, 0b00100000, 0b00010000}, // r
    {0b00010010, 0b00101010, 0b00101010, 0b00101010, 0b00100100}, // s
    {0b00000000, 0b00100000, 0b11111100, 0b00100010, 0b00000100}, // t
    {0b00111100, 0b00000010, 0b00000010, 0b00000100, 0b00111110}, // u
    {0b00111000, 0b00000100, 0b00000010, 0b00000100, 0b00111000}, // v
    {0b00111100, 0b00000010, 0b00001100, 0b00000010, 0b00111100}, // w
    {0b00100010, 0b00010100, 0b00001000, 0b00010100, 0b00100010}, // x
    {0b00110000, 0b00001010, 0b00001010, 0b00001010, 0b00111100}, // y
    {0b00100010, 0b00100110, 0b00101010, 0b00110010, 0b00100010}, // z
    {0b00010000, 0b00010000, 0b01101100, 0b10000010, 0b10000010}, // {
    {0b00000000, 0b00000000, 0b11111110, 0b00000000, 0b00000000}, // |
    {0b10000010, 0b10000010, 0b01101100, 0b00010000, 0b00010000}, // }
    {0b01000000, 0b10000000, 0b01000000, 0b01000000, 0b10000000}, // ~
    {0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // Delete
};

#endif /* FONT_5x7_H */
</pre>
<p>When we draw a character, we can subtract the value of SPACE &#8216; &#8216; from its decimal number and find its location in the font array. Then, we write 5 bytes of the font to the buffer.</p>
<pre>
/**
 * Draws a character at the position X
 *
 * @param uint8_t c The character to display
 * @param uint8_t x The position at the display from which to display
 */
void draw_char(uint8_t buf[], const uint8_t font[][APP_FONT_WIDTH], 
               uint8_t c, uint8_t x) 
{
    // Convert the character to an index
    c = c &#038; 0x7F;
    if (c < ' ') {
        c = 0;
    } else {
        c -= ' ';
    }

    for (int i = x, j = 0; j < APP_FONT_WIDTH; i++, j++) {
        buf[i] = font[c][j];
    }
}
</pre>
<p>We can display a character starting from the position 1 like this:</p>
<pre>
draw_char(buf, font, 'M', 1);
</pre>
<p>When all required characters are set in the display buffer, the display buffer is sent to the HT1632C controller at once.</p>
<pre>
display_write(display_dev, 0, 0, &#038;buf_desc, buf);
</pre>
<p>The code is available on <a href="https://github.com/faritka/zephyr-ht1632c" rel="noopener" target="_blank">https://github.com/faritka/zephyr-ht1632c</a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/led-matrix-font-ht1632c-zephyr-rtos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Writing HT1632 driver for RTOS Zephyr and Nucleo STM32</title>
		<link>https://hobby.farit.ru/ht1632-driver-rtos-zephyr-nucleo-stm32/</link>
		<comments>https://hobby.farit.ru/ht1632-driver-rtos-zephyr-nucleo-stm32/#comments</comments>
		<pubDate>Mon, 12 Jul 2021 21:16:31 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[ht1632c]]></category>
		<category><![CDATA[stm32]]></category>
		<category><![CDATA[zephyr]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=598</guid>
		<description><![CDATA[Zephyr Real-Time Operating System When you have a powerful microcontroller, there is no need to limit yourself by the capabilities of Arduino. Most Arduino programs run in a loop that checks buttons, does some calculations, connect to the Internet, etc sequentially. Coding gets complicated when you want to click on a button and process the [&#8230;]]]></description>
				<content:encoded><![CDATA[<h3>Zephyr Real-Time Operating System</h3>
<p>When you have a powerful microcontroller, there is no need to limit yourself by the capabilities of Arduino. Most Arduino programs run in a loop that checks buttons, does some calculations, connect to the Internet, etc sequentially. Coding gets complicated when you want to click on a button and process the click quickly while another function is slowly connecting and parsing a webpage. </p>
<p>A Real-Time operating system allows to code multiple functions and delegate the task of running them simultaneously to the OS.</p>
<p><a href="https://www.zephyrproject.org/" rel="noopener" target="_blank">Zephyr Project</a> is a very promising project. But some sensors, displays, hardware chips don&#8217;t have drivers to interact with them yet.</p>
<p>An experienced programmer usually learns by reading code of sample programs. Zephyr provides <a href="https://docs.zephyrproject.org/latest/" rel="noopener" target="_blank">many samples</a>.</p>
<h3>STM32 Nucleo boards</h3>
<p>ST provides $15 evaluation boards for its family of the STM32 ARM processors called Nucleo. </p>
<p>I compared <a href="https://www.st.com/en/evaluation-tools/stm32-nucleo-boards.html" rel="noopener" target="_blank">the list of available Nucleo boards</a> with <a href="https://docs.zephyrproject.org/latest/boards/" rel="noopener" target="_blank">the list of the Nucleo boards supported by Zephyr</a>. </p>
<p>I wanted a low-power microcontroller with the largest RAM and flash, and in the LQFP64 package (64 pins, 10&#215;10 mm), which is hot-air solderable. I selected <a href="https://docs.zephyrproject.org/latest/boards/arm/nucleo_l452re/doc" rel="noopener" target="_blank">ST Nucleo L452RE</a> for the project.</p>
<h3>How the Holtek HT1632C works</h3>
<p>The Holtek HT1632C is a popular display controller that can drive matrices of LEDs (one chip up to 32&#215;8 or 24&#215;16). <a href="https://www.holtek.com/productdetail/-/vg/HT1632D_32D-2" rel="noopener" target="_blank">The Holtek HT1632D</a> is a newer version that can come in a smaller LQFP48 7&#215;7 mm package that has fewer pins for rows.</p>
<p>Here is a typical electrical schema. The HT1632C controls the 5&#215;7 LED matrices (with 1 additional row for a dot) with the common cathodes.</p>
<p>Here is how it works. To turn on the 1st top LED in the 2nd column, the HT1632C sets 1 (HIGH) on ROW1 and opens COM0, so the current flows via the current-limiting resistor to the LED and then via COM0 to the ground. The LED turns on.</p>
<p>The LEDs in the matrices are addressed starting from the leftest top LED.</p>
<p>In order to enable the 1st top LED in the 2nd column, user code must write 1 into the HT1632C RAM address matching the ROW1 and COM0 combination. According to the HT1632C controller datasheet, the address is 02H when the controller is configured as 32 ROW x 8 COM or the address is 04H when the controller is configured as 24 ROW x 16 COM.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/07/display_LTP-305.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/07/display_LTP-305.png" alt="display LTP-305" width="832" height="945" class="aligncenter size-full wp-image-605" /></a></p>
<h3>The HT1632C communication protocol</h3>
<p>The HT1632C uses 4 pins to communicate with a microcontroller.</p>
<ul>
<li>CS (chip select)</li>
<li>WR (write)</li>
<li>RD (read)</li>
<li>DATA</li>
</ul>
<p>This driver doesn&#8217;t read from the display controller. It only writes. So, we don&#8217;t use the RD pin.</p>
<ul>
<li>The CS pin is active LOW. When the microcontroller wants to communicate with the HT1632C, it sets the CS pin to the ground (LOW).</li>
<li>The microcontroller sets the WR pin to LOW.</li>
<li>It sends a bit (0 or 1) by setting the DATA pin to LOW (0) or HIGH (1).</li>
<li>It sets the WR pin to HIGH. The HT1632C reads the DATA bit on the rising WR edge.</li>
<li>The microcontroller repeats the WR LOW, DATA bit, WR HIGH sequence until all bits are sent.</li>
<li>When it finishes, it sets the CS pin HIGH.</li>
</ul>
<p>There are two modes of operation: the command mode and the write data mode.</p>
<p>The command mode starts with the 3 bits 100 and then the command itself.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/07/command_mode.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/07/command_mode.png" alt="HT1632C command mode" width="1570" height="477" class="aligncenter size-full wp-image-610" /></a></p>
<p>The write data mode starts with the 3 bits 101, the RAM address, the 4 bits of data.</p>
<p>This driver writes the whole array of data starting from the address 00H using the Successive Address Writing Mode. It starts with the 3 bits 101, the RAM address 00H, then all 32 8-bit words or 24 16-bit words.</p>
<p>It&#8217;s the task of the application code to set individual bits in the matrix array. Then it sends the whole matrix array to the driver.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/07/write_data_mode.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/07/write_data_mode.png" alt="HT1632C write data mode" width="1469" height="358" class="aligncenter size-full wp-image-611" /></a></p>
<p>Here is how the command SYS EN (Turn on system oscillator) looks like on the screen of my oscilloscope.<br />
SYS EN 100-0000-0001-0</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/07/sending_command_sys_en.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/07/sending_command_sys_en.png" alt="HT1632C command SYS EN" width="1050" height="434" class="aligncenter size-full wp-image-615" /></a></p>
<h3>Zephyr Application Structure</h3>
<p>The structure of my application is based on <a href="https://github.com/zephyrproject-rtos/example-application/" rel="noopener" target="_blank">the Zephyr Example Application</a>.</p>
<p>It creates an application with a sensor driver.</p>
<p>I changed the string &#8216;example-application&#8217; in the files to my own application name.</p>
<h3>HT1632C Zephyr Driver Installation</h3>
<p>Download the source from <a href="https://github.com/faritka/zephyr-ht1632c" rel="noopener" target="_blank">https://github.com/faritka/zephyr-ht1632c</a> and follow instructions there.</p>
<h3>Zephyr driver configuration files</h3>
<p>Available configuration options are defined in the file <i>dts/bindings/display/holtek,ht1632c.yaml</i>.<br />
It follows the Device Tree specifications.</p>
<p>The GPIOS have the phandle-array type that describes the GPIO identifier, pin, and flags. For example, the flag GPIO_OUTPUT_HIGH sets the default output as HIGH, the flag GPIO_ACTIVE_LOW means when you set the pin ACTIVE, the signal goes LOW (the CS pin is active low). </p>
<pre>
description:  Holtek HT1632C display controller

compatible: "holtek,ht1632c"

include: base.yaml

properties:
    cs-gpios:
      type: phandle-array
      required: true
      description: GPIO to which the CS pin of HT1632C is connected.

    wr-gpios:
      type: phandle-array
      required: true
      description: GPIO to which the WR pin of HT1632C is connected.

    data-gpios:
      type: phandle-array
      required: true
      description: GPIO to which the DATA pin of HT1632C is connected.

    commons-options:
      type: int
      required: true
      default: 0x00
      description: |
        0x00: N-MOS  opendrain output and 8 common option
        0x01: N-MOS  opendrain  output  and  16 common option
        0x10: P-MOS  opendrain output and 8 common option
        0x11: P-MOS  opendrain  output  and  16 common option
</pre>
<p>The test application is located in the directory <i>app</i>.<br />
I created the overlay configuration file for my Nucleo L452RE board: <i>app/boards/nucleo_l452re.overlay</i>.</p>
<p>I defined the GPIOs that are connected to the HT1632C controller on the Nucleo L452RE board: CS to PB3, WR to PB5, DATA to PB4. The commons-options configures the HT1632C controller as 32&#215;8 N-MOS. This configuration doesn&#8217;t need external transistors, the current will flow from ROWN -> resistor -> LEDs -> COMN.</p>
<pre>
/ {
    ht1632c {
        compatible = "holtek,ht1632c";
        label = "HT1632C";
        cs-gpios = <&#038;gpiob 3 0>;
        wr-gpios = <&#038;gpiob 5 0>;
        data-gpios = <&#038;gpiob 4 0>;
        commons-options = <0x00>;
    };
};
</pre>
<h3>Hardware delays</h3>
<p>The HT1632C datasheet provides the table with electrical parameters.</p>
<p>t<sub>CLK</sub> is 500 ns &#8211; it&#8217;s the pulse width of each WR signal<br />
t<sub>su</sub> is minimum 100 ns, I set it to 250 ns &#8211; it&#8217;s the delay between the setting the DATA pin and the WR pin going HIGH.</p>
<p>The function <i>ht1632c_ns_to_sys_clock_hw_cycles</i> converts nanoseconds to the processor clock cycles that must pass during those nanoseconds.</p>
<pre>
/**
 * @brief Converts nanoseconds to the number of the processor system cycles
 *
 * @param uint32_t ns Nanoseconds
 *
 */
static inline uint32_t ht1632c_ns_to_sys_clock_hw_cycles(uint32_t ns)
{
    return ((uint64_t)sys_clock_hw_cycles_per_sec() * (ns) / NSEC_PER_SEC + 1);
}
</pre>
<p>That&#8217;s what I get for the Nucleo L452RE with the 80MHz clock.</p>
<pre>
Delay t<sub>CS</sub> 33
Delay t<sub>CLK</sub> 41
Delay t<sub>SU</sub> 21
Delay t<sub>H</sub> 21
Delay t<sub>SU1</sub> 25
Delay t<sub>H1</sub> 17
</pre>
<p>The function ht1632c_delay then delays execution counting cycles.</p>
<pre>
/**
 * @brief Delays the execution waiting for processor cycles
 *
 * @param dev Pointer to device data
 * @param uint32_t cycles_to_wait How many processor cycles to wait
 *
 */
static void ht1632c_delay(uint32_t cycles_to_wait)
{
    uint32_t start = k_cycle_get_32();

    // Wait until the given number of cycles have passed
    while (k_cycle_get_32() - start < cycles_to_wait) {
    }
}
</pre>
<p>Here is the function that writes a command to the HT1632C controller.</p>
<pre>
/**
 * @brief Writes a command to HT1632C
 *
 * @param dev Pointer to device data
 * @param uint8_t command Command without the first 3 bits 100
 *
 */
static void ht1632c_write_command(struct ht1632c_data *data, 
        uint8_t command)
{
    //CS down
    ht1632c_delay(data->delays->cs);
    ht1632c_set_cs_pin(data, true);
    ht1632c_delay(data->delays->su1);
    
    //100 - command mode
    ht1632c_write_bits(data, HT1632C_COMMAND_HEADER, BIT(2));
    //the command itself
    ht1632c_write_bits(data, command, BIT(7));
    //one extra bit
    ht1632c_write_bits(data, 0, BIT(0));

    //set the DATA pin low waiting for the next command
    ht1632c_set_data_pin(data, false);

    //CS UP
    ht1632c_delay(data->delays->h1);
    ht1632c_set_cs_pin(data, false);
}
</pre>
<h3>Sample program</h3>
<p>The sample program writes the letter M, changes the brightness, and then makes the HT1632C sleep when CONFIG_PM=y and CONFIG_PM_DEVICE=y in <i>app/prj.conf</i>.</p>
<p>It's interesting to see how the second row of the letter M is half-lighted in the photo. That's because the HT1632C constantly switches ROWs and COMs ON and OFF. The human eye can't catch it, but a photo camera can.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/07/ht132c_sample.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/07/ht132c_sample.png" alt="ht1632c sample program output" width="1600" height="1362" class="aligncenter size-full wp-image-626" /></a></p>
<h3>Useful links</h3>
<p><a href="https://interrupt.memfault.com/blog/building-drivers-on-zephyr?" rel="noopener" target="_blank">How to Build Drivers for Zephyr</a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/ht1632-driver-rtos-zephyr-nucleo-stm32/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>BMW E30 E36 White Flashlight Li-ion Battery 82119413147</title>
		<link>https://hobby.farit.ru/bmw-e30-e36-white-flashlight-li-ion-battery-82119413147/</link>
		<comments>https://hobby.farit.ru/bmw-e30-e36-white-flashlight-li-ion-battery-82119413147/#comments</comments>
		<pubDate>Wed, 23 Jun 2021 22:06:14 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[bmw e30]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[e30]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=563</guid>
		<description><![CDATA[The BMW E30, E32, E34, and E36 have space in the glove box for the white flashlight (torch) 63171375457 or 82119413147. The later BMW models provide the black flashlight 63316962052, which is not compatible because the flashlight contacts are male. The original electric circuit is extremely simple. There are two 1.2V NiMH coin cells, a [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>The BMW E30, E32, E34, and E36 have space in the glove box for the white flashlight (torch) 63171375457 or 82119413147. The later BMW models provide the black flashlight 63316962052, which is not compatible because the flashlight contacts are male.</p>
<p>The original electric circuit is extremely simple. There are two 1.2V NiMH coin cells, a resistor to constantly charge them, an E10 2.4V bulb, and a switch.<br />
You can read an article about <a href="https://surfncircuits.com/2017/07/23/teardown-and-repair-of-the-simply-designed-bmw-e36-glovebox-flashlight/" rel="noopener" target="_blank">Teardown and Repair of the Simply Designed BMW E36 Glovebox Flashlight</a>. </p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/06/bmw-e30-flashlight-63171375457.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/bmw-e30-flashlight-63171375457.png" alt="bmw e30 e32 e34 e36 flashlight 63171375457" width="1600" height="1558" class="aligncenter size-full wp-image-564" /></a></p>
<p>All white flashlights that you can get now have dead batteries. You can replace the batteries with similar NiMH batteries, and they should work for a few years.</p>
<p>My project replaces the original circuit with a more sophisticated circuit that has a charger chip, Li-ion 4.2V coin cells, and an LED bulb.</p>
<h3>Electrical Circuit</h3>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-charger-ltc4079.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-charger-ltc4079.png" alt="flashlight charger ltc4079" width="1104" height="727" class="aligncenter size-full wp-image-651" /></a></p>
<p>The charging chip is <a href="https://www.analog.com/en/products/ltc4079.html" rel="noopener" target="_blank">LTC4079</a>. </p>
<blockquote><p>The LTC®4079 is a low quiescent current, high voltage linear charger for most battery chemistry types including Li-Ion/Polymer, LiFePO4, Lead-Acid or NiMH battery stacks up to 60V. The maximum charge current is adjustable from 10mA  to  250mA  with  an  external  resistor.  The  battery  charge voltage is set using an external resistor divider.</p></blockquote>
<p>The chip is tiny, but it&#8217;s possible to solder the QFN chip using hot air.</p>
<p>Two ⌀24.5mm <a href="https://www.illinoiscapacitor.com/ic_search/battery_products_detail.aspx?icpartnumber=16" rel="noopener" target="_blank">RJD2450ST1</a> 200 mAh rechargeable coin cells are used. </p>
<blockquote><p>Charging Current &#8211; Standard: 95 mA<br />
Charging Voltage &#8211; Maximum: 4.2 V</p></blockquote>
<p>The RJD2450ST1 has contacts for soldered wires. But it requires cutting two small slots in the plastic circle in the middle of the flashlight. The RJD2450 without contacts can be used if there is a battery spot welder for the original contacts.</p>
<p>The resistor divider R2=1.54MΩ and R4=249kΩ provides the 8.4V charging voltage.<br />
The resistor divider R1=1.2MΩ and R3=130kΩ disables charging when the battery voltage goes down to less than 12.17V. V<sub>IN(REG)</sub> = 1.190 × (1 + 1200000 ÷ 130000).<br />
The resistor R5=12kΩ sets the charging current to 25mA. R<sub>PROG</sub> = 297.5 ÷ 0.025. The maximum charging current for the coin cells is 95mA. When I tested this amperage by using a 3kΩ resistor, the LTC4079 was very hot as the PCB is too tiny to dissipate all heat.<br />
The timer capacitor C2=0.1µF is set to disable charging after approximately 5½ hours. C<sub>TIMER</sub> = 5.5 × 18.2.<br />
The 10k NTC thermistor is NHQM103B375T5.</p>
<h3>PCB</h3>
<p>I used a 4-layer 1.2mm-thick PCB. </p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-charger-pcb.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-charger-pcb.png" alt="flashlight charger pcb" width="1557" height="1024" class="aligncenter size-full wp-image-652" /></a></p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-charger-pcb-3d.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-charger-pcb-3d.png" alt="flashlight charger pcb 3d LTC4079" width="1469" height="973" class="aligncenter size-full wp-image-653" /></a></p>
<h3>LED bulb</h3>
<p>The flashlight uses E10 bulbs. </p>
<p>The default polarity of the BMW E30 flashlight bulb is to have &#8211; (negative or ground) at the tip of the bulb and to have + (positive) at the screw body. Most LED E10 bulbs have the opposite polarity: + at the tip and &#8211; at the body. </p>
<p>It&#8217;s relatively easy to change the wires, but the original connectors around the coin cells must be isolated well.</p>
<p>I checked a 1W LED light. It was really hot when it was on. The body of the flashlight is plastic and there is no airflow, so the plastic can melt.</p>
<p>I selected <a href="https://www.aliexpress.com/item/1005002604945130.html" rel="noopener" target="_blank">this LED</a>: 0.5W, Screw E10, Nopolar, 12V Warm White(4300K). It can be connected to &#8211; and + both ways. It doesn&#8217;t get warm. It also has the same height as the original bulb: 22mm. It even has a lens.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/06/e10-led.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/e10-led.png" alt="e10 led" width="1145" height="1145" class="aligncenter size-full wp-image-582" /></a></p>
<h3>Pictures</h3>
<p>The flashlight is pretty bright and works for more than 10 hours on one charge.</p>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2021/06/rjd2450st1.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/rjd2450st1.png" alt="rjd2450st1" width="1600" height="1045" class="aligncenter size-full wp-image-586" /></a><br />
<a href="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-inside.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-inside.png" alt="flashlight-inside" width="1600" height="1000" class="aligncenter size-full wp-image-587" /></a><br />
<a href="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-inside-led.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/flashlight-inside-led.png" alt="flashlight-inside-led" width="2548" height="1420" class="aligncenter size-full wp-image-588" /></a><br />
<a href="http://hobby.farit.ru/wp-content/uploads/2021/06/e30-flashlight-led.png"><img src="http://hobby.farit.ru/wp-content/uploads/2021/06/e30-flashlight-led.png" alt="e30-flashlight-led" width="1600" height="1369" class="aligncenter size-full wp-image-589" /></a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/bmw-e30-e36-white-flashlight-li-ion-battery-82119413147/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Qt5 GUI on Intel Edison</title>
		<link>https://hobby.farit.ru/qt5-gui-intel-edison/</link>
		<comments>https://hobby.farit.ru/qt5-gui-intel-edison/#comments</comments>
		<pubDate>Mon, 06 Jun 2016 06:27:46 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[intel edison]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=159</guid>
		<description><![CDATA[If you want to create a graphical user interface (GUI) for the Intel Edison, it&#8217;s better to use the popular Qt cross-platform framework. Then you will be able to develop and debug a program on your powerful desktop computer and then deploy it to your Edison and test it there. My hardware and Linux kernel [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>If you want to create a graphical user interface (GUI) for the Intel Edison, it&#8217;s better to use the popular <a href="https://www.qt.io/" target="_blank">Qt cross-platform framework</a>.<br />
Then you will be able to develop and debug a program on your powerful desktop computer and then deploy it to your Edison and test it there.</p>
<h5>My hardware and Linux kernel configuration</h5>
<p>I used my shield that has a SSD1322 based display connected to the SPI bus and 4 push buttons connected to GPIOs.<br />
The display is controlled through <a href="http://hobby.farit.ru/linux-framebuffer-fbtft-with-dma-for-intel-edison/" target="_blank">the Linux framebuffer kernel module fbtft</a>.<br />
The buttons are controlled through <a href="http://hobby.farit.ru/connecting-buttons-intel-edison/" target="_blank">the gpio-keys module</a>.</p>
<h5>QT5 Yocto Client package installation</h5>
<p>My Edison source packages are located in the folder ~/edison.</p>
<p>I increased the size of my root Edison partition in the file ~/edison/poky/meta-intel-edison/meta-intel-edison-distro/recipes-core/images/edison-image.bb</p>
<pre class="brush: plain; title: ; notranslate">
IMAGE_ROOTFS_SIZE = &quot;1048576&quot;
</pre>
<p>I downloaded a new meta package meta-qt5.<br />
At the time of writing, the Intel Edison packages used the openembedded branch called &#8220;dizzy&#8221;.</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/edison/poky
git clone -b dizzy https://github.com/meta-qt5/meta-qt5.git
</pre>
<p>I modified the configuration file ~/edison/build_edison/conf/auto.conf<br />
I added some Qt packages and enabled some configuration options. I may select something else in the future.</p>
<p>The Intel Edison has no GPU. So, the Qt OpenGL based modules don&#8217;t work. Among them are Qt Quick and Qt Declarative and the packages that depend on these two.</p>
<pre class="brush: cpp; title: ; notranslate">
DISTRO = &quot;poky-edison&quot;
MACHINE = &quot;edison&quot;
DISTRO_FEATURES_append = &quot; alsa bluetooth x11&quot;
PACKAGE_CLASSES = &quot;package_ipk&quot;
BB_DANGLINGAPPENDS_WARNONLY = &quot;1&quot;
EDISONREPO_TOP_DIR = &quot;${TOPDIR}/../poky&quot;

IMAGE_INSTALL_append = &quot; tinyb&quot;
IMAGE_INSTALL_append = &quot; tinyb-dev&quot;
#IMAGE_INSTALL_append = &quot; openzwave&quot;
IMAGE_INSTALL_append = &quot; bacnet-stack&quot;
IMAGE_INSTALL_append = &quot; libmodbus&quot;
IMAGE_INSTALL_append = &quot; mc&quot;
IMAGE_INSTALL_append = &quot; qtbase qtbase-fonts \
    qtbase-plugins \
    qtbase-tools \
    qtimageformats-plugins&quot;
IMAGE_INSTALL_append_core2-32 = &quot; libft4222&quot;
IMAGE_INSTALL_append_edison = &quot; libft4222&quot;
LICENSE_FLAGS_WHITELIST_append = &quot; ftdi&quot;
GCCVERSION = &quot;4.9%&quot;

PACKAGECONFIG_DISTRO_append_pn-qtbase = &quot; linuxfb icu alsa pulseaudio sql-sqlite&quot;
</pre>
<p>I modified the file ~/edison/build_edison/conf/bblayers.conf and added the meta-qt5 layer at the end of BBLAYERS.</p>
<pre class="brush: cpp; title: ; notranslate"> 
LCONF_VERSION = &quot;6&quot;

BBPATH = &quot;${TOPDIR}&quot;
BBFILES ?= &quot;&quot;

BBLAYERS ?= &quot; \
  ${TOPDIR}/../poky/meta \
  ${TOPDIR}/../poky/meta-intel-edison/meta-intel-arduino \
  ${TOPDIR}/../poky/meta-intel-edison/meta-intel-edison-bsp \
  ${TOPDIR}/../poky/meta-intel-edison/meta-intel-edison-distro \
  ${TOPDIR}/../poky/meta-intel-iot-devkit \
  ${TOPDIR}/../poky/meta-intel-iot-middleware \
  ${TOPDIR}/../poky/meta-java \
  ${TOPDIR}/../poky/meta-oic \
  ${TOPDIR}/../poky/meta-openembedded/meta-filesystems \
  ${TOPDIR}/../poky/meta-openembedded/meta-networking \
  ${TOPDIR}/../poky/meta-openembedded/meta-oe \
  ${TOPDIR}/../poky/meta-openembedded/meta-python \
  ${TOPDIR}/../poky/meta-openembedded/meta-ruby \
  ${TOPDIR}/../poky/meta-openembedded/meta-webserver \
  ${TOPDIR}/../poky/meta-yocto \
  ${TOPDIR}/../poky/meta-yocto-bsp \
  ${TOPDIR}/../poky/meta-qt5 \
  &quot;
BBLAYERS_NON_REMOVABLE ?= &quot; \
  ${TOPDIR}/../poky/meta \
  ${TOPDIR}/../poky/meta-yocto \
  &quot;
</pre>
<p>Then the usual Edison image compilation stage.<br />
You must insert yourself into the group &#8220;dialout&#8221; on Linux Ubuntu to have permissions to run the script &#8220;flashall.sh&#8221;.</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/edison/poky/
source oe-init-build-env ../build_edison/
bitbake edison-image u-boot
../poky/meta-intel-edison/utils/flash/postBuild.sh .
./toFlash/flashall.sh
</pre>
<h5>Qt5 Host cross-platform SDK compilation</h5>
<p>I modified the file ~/edison/poky/meta-qt5/recipes-qt/packagegroups/packagegroup-qt5-toolchain-target.bb<br />
and deleted all packages from the list RDEPENDS_${PN} that depend on Qt Quick and Qt Declarative. My list may change in the future.</p>
<pre class="brush: cpp; title: ; notranslate">
RDEPENDS_${PN} += &quot; \
    packagegroup-core-standalone-sdk-target \
    libsqlite3-dev \
    qtbase-dev \
    qtbase-fonts \
    qtbase-mkspecs \
    qtbase-plugins \
    qtbase-staticdev \
    qtconnectivity-dev \
    qtconnectivity-mkspecs \
    qtimageformats-dev \
    qtimageformats-plugins \
    qtserialport-dev \
    qtserialport-mkspecs \
    qtsvg-dev \
    qtsvg-mkspecs \
    qtsvg-plugins \
    qtsystems-dev \
    qtsystems-mkspecs \
    qttools-dev \
    qttools-mkspecs \
    qttools-staticdev \
    qttools-tools \
    qtxmlpatterns-dev \
    qtxmlpatterns-mkspecs \
&quot;
</pre>
<p>I compiled the SDK.</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/edison/poky/
source oe-init-build-env ../build_edison/
bitbake meta-toolchain-qt5
</pre>
<p>I ran the new SDK installer in ~/edison/build_edison/tmp/deploy/sdk</p>
<pre class="brush: bash; title: ; notranslate">
sh poky-edison-glibc-x86_64-meta-toolchain-qt5-core2-32-toolchain-1.7.3.sh
</pre>
<p>It installed the SDK into the default folder /opt/poky-edison/1.7.3<br />
I opened the directory /opt/poky-edison/1.7.3/sysroots/x86_64-pokysdk-linux/usr/bin/i586-poky-linux<br />
and created symlinks. Without them, my system compiler and linker in /usr/bin are called instead.</p>
<pre class="brush: bash; title: ; notranslate">
ln -s i586-poky-linux-g++ g++
ln -s i586-poky-linux-cpp cpp
ln -s i586-poky-linux-ld ld
ln -s i586-poky-linux-gdb gdb
</pre>
<h5>Qt Creator IDE installation</h5>
<p>I installed Qt Creator from <a href="https://download.qt.io/archive/qt/5.3/5.3.2/" target="_blank">https://download.qt.io/archive/qt/5.3/5.3.2/</a>, as the version of Qt in the Yocto package is 5.3.2.<br />
But, probably, the one from the Linux distribution should work as well.<br />
I installed it in the directory ~/Qt.</p>
<p>I modified the script ~/Qt/Tools/QtCreator/bin/qtcreator.sh, so it will load the environment variables for the SDK. I put the new line at the very top of the script.</p>
<pre class="brush: bash; title: ; notranslate">
source /opt/poky-edison/1.7.3/environment-setup-core2-32-poky-linux
#! /bin/sh
</pre>
<p>On my Edison I created the user &#8220;farit&#8221; and added him to the group &#8220;video&#8221; that has permissions to write to the framebuffer and read input buttons. His home directory on the Edison is &#8220;/home/farit&#8221;.</p>
<pre class="brush: bash; title: ; notranslate">
usermod -a -G video farit
</pre>
<p>I modified the menu link on my desktop to run the script ~/Qt/Tools/QtCreator/bin/qtcreator.sh to open Qt Creator.</p>
<p>In Qt Creator, I opened the menu Tools -> Options -> Devices and added my Edison device as &#8220;Generic Linux Device&#8221;.<br />
I named it &#8220;edison&#8221; and provided the name and password combination for the user &#8220;farit&#8221;. Then tested the connection.</p>
<p>I opened the menu Tools > Options > Build &#038; Run.<br />
I opened the tab &#8220;Qt Versions&#8221;, clicked on the button &#8220;Add&#8221; and browsed to select qmake</p>
<pre class="brush: plain; title: ; notranslate">
/opt/poky-edison/1.7.3/sysroots/x86_64-pokysdk-linux/usr/bin/qt5/qmake
</pre>
<p>I opened the tab &#8220;Compilers&#8221;, clicked on the button &#8220;Add&#8221;, selected &#8220;GCC&#8221; and browsed to select gcc</p>
<pre class="brush: plain; title: ; notranslate">
/opt/poky-edison/1.7.3/sysroots/x86_64-pokysdk-linux/usr/bin/i586-poky-linux/i586-poky-linux-gcc
</pre>
<p>I opened the tab &#8220;Debuggers&#8221;, clicked on the button &#8220;Add&#8221; and browsed to select gdb. I named it GDB.</p>
<pre class="brush: plain; title: ; notranslate">
/opt/poky-edison/1.7.3/sysroots/x86_64-pokysdk-linux/usr/bin/i586-poky-linux/i586-poky-linux-gdb
</pre>
<p>I opened the tab &#8220;Kits&#8221;, clicked on the button &#8220;Add&#8221; and filled in the form.<br />
I selected the values for Qt, Compiler, Debugger that I just defined in the previous steps.</p>
<p>Name: edison<br />
Device type: Generic Linux Device<br />
Device: edison<br />
Sysroot: /opt/poky-edison/1.7.3/sysroots/x86_64-pokysdk-linux<br />
Compiler: GCC<br />
Debugger: GDB<br />
Qt Version: Qt 5.3.2</p>
<h5>Sample Qt program</h5>
<p>I clicked on &#8220;New Project&#8221; and selected Applications -> Qt Widgets Application.<br />
I named it &#8220;testik&#8221;.</p>
<p>In testik.pro, I added the path &#8220;/home/farit&#8221;, where Qt Creator will copy the executable on the Edison.</p>
<pre class="brush: cpp; title: ; notranslate">
#-------------------------------------------------
#
# Project created by QtCreator 2016-06-02T14:32:35
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = testik
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

target.path = /home/farit
INSTALLS += target
</pre>
<p>In mainwindow.h, I only added the prototype for the function &#8220;keyPressEvent&#8221;.</p>
<pre class="brush: cpp; title: ; notranslate">
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include &lt;QMainWindow&gt;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
    void keyPressEvent(QKeyEvent *);

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
</pre>
<p>main.cpp is the default one</p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;mainwindow.h&quot;
#include &lt;QApplication&gt;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}
</pre>
<p>In mainwindow.cpp, I implemented the function &#8220;keyPressEvent&#8221;.</p>
<pre class="brush: cpp; title: ; notranslate">
#include &lt;QKeyEvent&gt;
#include &lt;QDebug&gt;
#include &quot;mainwindow.h&quot;
#include &quot;ui_mainwindow.h&quot;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui-&gt;setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if(event-&gt;key() == Qt::Key_Up)
    {
        if (event-&gt;isAutoRepeat()) {
             ui-&gt;myLabel-&gt;setText(&quot;You auto pressed Up&quot;);
        }
        else {
            ui-&gt;myLabel-&gt;setText(&quot;You pressed Up&quot;);
        }
    }
    else if(event-&gt;key() == Qt::Key_Down)
    {
        ui-&gt;myLabel-&gt;setText(&quot;You pressed Down&quot;);
    }
}
</pre>
<p>In mainwindow.ui, I created a label &#8220;myLabel&#8221;.<br />
In the MainWindow properties, I clicked on the &#8220;palette&#8221; button and made the background transparent by setting the opacity to 0 for &#8220;Window&#8221;. I changed &#8220;WindowText&#8221; to white by selecting the color #FFFFFF.</p>
<p>In the &#8220;Projects&#8221; tab in the &#8220;Run&#8221; section, I added the arguments for the executable, so it can write to the default framebuffer /dev/fb0 and read input from /dev/input/event0.</p>
<pre class="brush: plain; title: ; notranslate">
-platform linuxfb -plugin EvdevKeyboard
</pre>
<figure id="attachment_163" style="width: 834px;" class="wp-caption aligncenter"><a href="http://hobby.farit.ru/wp-content/uploads/2016/06/qt_test.png"><img src="http://hobby.farit.ru/wp-content/uploads/2016/06/qt_test-834x854.png" alt="Qt test program running" width="834" height="854" class="size-medium wp-image-163" /></a><figcaption class="wp-caption-text">Qt test program running</figcaption></figure>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/qt5-gui-intel-edison/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Connecting Buttons to Intel Edison</title>
		<link>https://hobby.farit.ru/connecting-buttons-intel-edison/</link>
		<comments>https://hobby.farit.ru/connecting-buttons-intel-edison/#comments</comments>
		<pubDate>Sun, 05 Jun 2016 06:29:55 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[intel edison]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=152</guid>
		<description><![CDATA[The Intel Edison has multiple GPIO pins, which can be connected to push buttons. I use the gpio-keys Linux driver. Each button emits an event, which can be read in the same way as a keyboard event. Schematic Here is the schematic of my shield with the push buttons. The buttons are SW2, SW3, SW4, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>The Intel Edison has multiple GPIO pins, which can be connected to push buttons.</p>
<p>I use the gpio-keys Linux driver. Each button emits an event, which can be read in the same way as a keyboard event.</p>
<h5>Schematic</h5>
<p>Here is the schematic of my shield with the push buttons. The buttons are SW2, SW3, SW4, SW5.<br />
<figure id="attachment_104" style="width: 854px;" class="wp-caption aligncenter"><a href="http://hobby.farit.ru/wp-content/uploads/2016/04/display_schematic.png"><img class="size-medium wp-image-104" src="http://hobby.farit.ru/wp-content/uploads/2016/04/display_schematic-854x604.png" alt="Shiled with push buttons for Intel Edison" width="854" height="604" /></a><figcaption class="wp-caption-text">Shield with Push Buttons for Intel Edison</figcaption></figure></p>
<h5>GPIO pullup code</h5>
<p>As you can see, one side of a button is connected to the ground and another to a GPIO pin. I could use external pullup resistors, but the Intel Edison has built-in pullup resistors, which I activated instead. The current kernel code doesn&#8217;t have a function for setting a pullup resitor, so I had to write my own.</p>
<p>I added the function lnw_gpio_set_pull_alt into the Linux kernel file drivers/gpio/gpio-langwell.c</p>
<pre class="brush: cpp; title: ; notranslate">
void lnw_gpio_set_pull_alt(unsigned gpio, int value, int pullup_value)
{
	struct lnw_gpio *lnw;
	u32 flis_offset;
	u32 flis_value;
	unsigned long flags;

	/* use this trick to get memio */
	lnw = irq_get_chip_data(gpio_to_irq(gpio));
	if (!lnw) {
		pr_err(&quot;langwell_gpio: can not find pin %d\n&quot;, gpio);
		return;
	}
	if (gpio &lt; lnw-&gt;chip.base || gpio &gt;= lnw-&gt;chip.base + lnw-&gt;chip.ngpio) {
		dev_err(lnw-&gt;chip.dev, &quot;langwell_gpio: wrong pin %d to config alt\n&quot;, gpio);
		return;
	}
	gpio -= lnw-&gt;chip.base;

	if (lnw-&gt;type != TANGIER_GPIO) {
		return;
    }

	flis_offset = lnw-&gt;get_flis_offset(gpio);
	if (WARN(flis_offset == -EINVAL, &quot;invalid pin %d\n&quot;, gpio))
		return -EINVAL;
	if (is_merr_i2c_flis(flis_offset))
		return;

	spin_lock_irqsave(&amp;lnw-&gt;lock, flags);
	flis_value = get_flis_value(flis_offset);
	if (value) {
		flis_value |= PULLUP_ENABLE;
		flis_value &amp;= ~PULLDOWN_ENABLE;
	} else {
		flis_value |= PULLDOWN_ENABLE;
		flis_value &amp;= ~PULLUP_ENABLE;
	}
	//flis_value |= PUPD_VAL_50K;
    flis_value |= pullup_value;

	set_flis_value(flis_value, flis_offset);
	spin_unlock_irqrestore(&amp;lnw-&gt;lock, flags);
}
EXPORT_SYMBOL_GPL(lnw_gpio_set_pull_alt);
</pre>
<p>Also added its declaration and the constants for the pullup values into the Linux kernel file include/linux/lnw_gpio.h</p>
<pre class="brush: cpp; title: ; notranslate">
#define PUPD_VAL_2K	(0 &lt;&lt; 4)
#define PUPD_VAL_20K	(1 &lt;&lt; 4)
#define PUPD_VAL_50K	(2 &lt;&lt; 4)
#define PUPD_VAL_910	(3 &lt;&lt; 4)
void lnw_gpio_set_pull_alt(unsigned gpio, int value, int pullup_value);
</pre>
<p>I added the definitions for my 4 push buttons into the Linux kernel file arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c<br />
The buttons are for GPIO12, GPIO13, GPIO182, GPIO183.<br />
Each button configured as a general purpose pin by using the multiplexor code: lnw_gpio_set_alt(gb[i].gpio, LNW_GPIO);<br />
The pullup value is set to 50K using the previously written function: lnw_gpio_set_pull_alt(gb[i].gpio, 1, PUPD_VAL_50K);</p>
<pre class="brush: cpp; title: ; notranslate">
/*
 * platform_gpio_keys.c: gpio_keys platform data initilization file
 *
 * (C) Copyright 2008 Intel Corporation
 * Author:
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 */

#include &lt;linux/input.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/gpio_keys.h&gt;
#include &lt;linux/platform_device.h&gt;
#include &lt;asm/intel-mid.h&gt;
#include &quot;platform_gpio_keys.h&quot;
#include &lt;linux/lnw_gpio.h&gt;

/*
 * we will search these buttons in SFI GPIO table (by name)
 * and register them dynamically. Please add all possible
 * buttons here, we will shrink them if no GPIO found.
 */
static struct gpio_keys_button gpio_button[] = {
        {
                .code = KEY_POWER,
                .gpio = -1, /* GPIO number */
                .active_low = 1,
                .desc = &quot;power_btn&quot;,/*Button description*/
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 3000,
        },
        {
                .code = KEY_UP,
                .gpio = 182,
                .active_low = 1,
                .desc = &quot;up_btn&quot;,
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
        {
                .code = KEY_DOWN,
                .gpio = 12,
                .active_low = 1,
                .desc = &quot;down_btn&quot;,
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
        {
                .code = KEY_ESC,
                .gpio = 13,
                .active_low = 1,
                .desc = &quot;back_btn&quot;,
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
        {
                .code = KEY_ENTER,
                .gpio = 183,
                .active_low = 1,
                .desc = &quot;down_btn&quot;,
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
};

static struct gpio_keys_platform_data gpio_keys = {
	.buttons	= gpio_button,
	.rep		= 1,
	.nbuttons	= -1, /* will fill it after search */
};

static struct platform_device pb_device = {
	.name		= DEVICE_NAME,
	.id		= -1,
	.dev		= {
		.platform_data	= &amp;gpio_keys,
	},
};

/*
 * Shrink the non-existent buttons, register the gpio button
 * device if there is some
 */
static int __init pb_keys_init(void)
{
	struct gpio_keys_button *gb = gpio_button;
	int i, num, good = 0;

	num = sizeof(gpio_button) / sizeof(struct gpio_keys_button);
	for (i = 0; i &lt; num; i++) {
		pr_info(&quot;info[%2d]: name = %s, gpio = %d\n&quot;,
			 i, gb[i].desc, gb[i].gpio);
		if (gb[i].gpio == -1)
			continue;

                if (gb[i].gpio &gt; 0) {
                    lnw_gpio_set_alt(gb[i].gpio, LNW_GPIO);
                    lnw_gpio_set_pull_alt(gb[i].gpio, 1, PUPD_VAL_50K);
                }

		if (i != good)
			gb[good] = gb[i];
		good++;
	}

	if (good) {
		gpio_keys.nbuttons = good;
		return platform_device_register(&amp;pb_device);
	}
	return 0;
}
late_initcall(pb_keys_init);
</pre>
<h5>Testing Buttons</h5>
<p>When the new Linux kernel was recompiled and flashed to the Intel Edison, I could verify the buttons.</p>
<p>I checked the files in the directory for the GPIO12: /sys/kernel/debug/gpio_debug/gpio12<br />
current_pinmux was mode0<br />
current_pullmode was pullup<br />
current_pullstrength was 50k</p>
<p>I ran the command and tried to push on the buttons. I received some symbols. It meant that the buttons worked.</p>
<pre class="brush: bash; title: ; notranslate">
cat /dev/input/event0
</pre>
<p>I copied the file evtest.c from <a href="http://elinux.org/images/9/93/Evtest.c" target="_blank">http://elinux.org/images/9/93/Evtest.c</a> and compiled it on my Edison.</p>
<pre class="brush: bash; title: ; notranslate">
gcc evtest.c -o evtest
</pre>
<p>I ran the program evtest and got expected results.</p>
<pre class="brush: bash; title: ; notranslate">
# ./evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: &quot;gpio-keys&quot;
Supported events:
  Event type 0 (Sync)
  Event type 1 (Key)
    Event code 1 (Esc)
    Event code 28 (Enter)
    Event code 103 (Up)
    Event code 108 (Down)
  Event type 20 (Repeat)
Testing ... (interrupt to exit)
Event: time 1465107735.152126, type 1 (Key), code 108 (Down), value 1
Event: time 1465107735.152126, -------------- Report Sync ------------
Event: time 1465107735.344576, type 1 (Key), code 108 (Down), value 0
Event: time 1465107735.344576, -------------- Report Sync ------------
Event: time 1465107743.087594, type 1 (Key), code 103 (Up), value 1
Event: time 1465107743.087594, -------------- Report Sync ------------
Event: time 1465107743.332727, type 1 (Key), code 103 (Up), value 0
Event: time 1465107743.332727, -------------- Report Sync ------------
Event: time 1465107745.988003, type 1 (Key), code 28 (Enter), value 1
Event: time 1465107745.988003, -------------- Report Sync ------------
Event: time 1465107746.187692, type 1 (Key), code 28 (Enter), value 0
Event: time 1465107746.187692, -------------- Report Sync ------------
Event: time 1465107748.336925, type 1 (Key), code 1 (Esc), value 1
Event: time 1465107748.336925, -------------- Report Sync ------------
Event: time 1465107748.552085, type 1 (Key), code 1 (Esc), value 0
Event: time 1465107748.552085, -------------- Report Sync ------------
</pre>
<p>I may change the event key codes for the buttons. I can get the codes from the same file <a href="http://elinux.org/images/9/93/Evtest.c" target="_blank">http://elinux.org/images/9/93/Evtest.c</a></p>
<h5>Accessing /dev/input/event0 as a regular user</h5>
<p>By default, the device /dev/input/event0 is accessible only by root.</p>
<p>I created the file /etc/udev/rules.d/input.rules<br />
I chose the group &#8220;video&#8221; as I will use a user who should be able to access both the video device and the input device.</p>
<pre class="brush: plain; title: ; notranslate">
KERNEL==&quot;event*&quot;, NAME=&quot;input/%k&quot;, MODE=&quot;660&quot;, GROUP=&quot;video&quot;
</pre>
<p>I also created the file with the same contents poky/meta/recipes-core/systemd/systemd/input.rules<br />
and added it into the recipe poky/meta/recipes-core/systemd/systemd_216.bb</p>
<pre class="brush: plain; title: ; notranslate">
SRC_URI = &quot;git://anongit.freedesktop.org/systemd/systemd;branch=master;protocol=git \
           file://binfmt-install.patch \
           file://systemd-pam-configure-check-uclibc.patch \
           file://systemd-pam-fix-execvpe.patch \
           file://systemd-pam-fix-fallocate.patch \
           file://systemd-pam-fix-mkostemp.patch \
           file://optional_secure_getenv.patch \
           file://uclibc-sysinfo_h.patch \
           file://uclibc-get-physmem.patch \
           file://0001-add-support-for-executing-scripts-under-etc-rcS.d.patch \
           file://0001-missing.h-add-fake-__NR_memfd_create-for-MIPS.patch \
           file://0001-Make-root-s-home-directory-configurable.patch \
           file://0001-systemd-user-avoid-using-system-auth.patch \
           file://0001-journal-Fix-navigating-backwards-missing-entries.patch \
           file://0001-tmpfiles-make-resolv.conf-entry-conditional-on-resol.patch \
           file://0001-build-sys-do-not-install-tmpfiles-and-sysusers-files.patch \
           file://0001-build-sys-configure-the-list-of-system-users-files-a.patch \
           file://touchscreen.rules \
           file://input.rules \
           file://00-create-volatile.conf \
           file://init \
           file://run-ptest \
          &quot;
</pre>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/connecting-buttons-intel-edison/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Linux Framebuffer fbtft with SPI DMA for Intel Edison</title>
		<link>https://hobby.farit.ru/linux-framebuffer-fbtft-with-dma-for-intel-edison/</link>
		<comments>https://hobby.farit.ru/linux-framebuffer-fbtft-with-dma-for-intel-edison/#comments</comments>
		<pubDate>Tue, 31 May 2016 02:43:50 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[fbtft]]></category>
		<category><![CDATA[intel edison]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=140</guid>
		<description><![CDATA[When you want to connect a display to the Intel Edison module, you should utilise the existing Linux infrastructure and use a kernel framebuffer driver instead of writing your own screen functions based on Arduino libraries. The schematics and more technical information about my display and board are in my previous post: Framebuffer fbtft Installation [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>When you want to connect a display to the Intel Edison module, you should utilise the existing Linux infrastructure and use a kernel framebuffer driver instead of writing your own screen functions based on Arduino libraries.</p>
<p>The schematics and more technical information about my display and board are in my previous post: <a href="http://hobby.farit.ru/framebuffer-fbtft-installation-intel-edison-oled-display-ssd1322/" target="_blank">Framebuffer fbtft Installation on Intel Edison for OLED Display SSD1322</a>.</p>
<p>At the time of writing the article, the Linux kernel SPI support was broken in the original Intel kernel. So, I used <a href="https://github.com/primiano/edison-kernel" target="_blank">Primiano&#8217;s kernel</a> that fixed the SPI support. It works perfectly for me, it may not contain the latest Intel&#8217;s modifications. Use it on your own risk.</p>
<p>First, I downloaded the Intel sources from <a href="http://iotdk.intel.com/src/3.0/edison/" target="_blank">http://iotdk.intel.com/src/3.0/edison/</a>. I extracted them into ~/edison in my home directory and followed the instructions for compiling it.</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/edison/poky/
source oe-init-build-env ../build_edison/
bitbake edison-image u-boot
../poky/meta-intel-edison/utils/flash/postBuild.sh .
./toFlash/flashall.sh
</pre>
<p>Then I replaced the kernel in ~/edison/poky/linux-kernel by my own, but I kept the directories .git and .meta intact.<br />
I increased the revision number in the variable PR = &#8220;r3&#8243; in the file ~/edison/poky/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/linux-externalsrc.bb<br />
I added my new configuration parameters for fbtft into the file ~/edison/poky/linux-kernel/arch/x86/configs/i386_edison_defconfig</p>
<p>I downloaded the <a href="https://github.com/notro/fbtft" target="_blank">fbtft sources</a>. I created the folder ~/edison/poky/linux-kernel/drivers/video/fbtft and extracted the fbtft sources into the folder.</p>
<p>I added the line in ~/edison/poky/linux-kernel/drivers/video/Kconfig before the line &#8220;endmenu&#8221;.</p>
<pre class="brush: cpp; title: ; notranslate">
source &quot;drivers/video/fbtft/Kconfig&quot;
</pre>
<p>I added the line in ~/edison/poky/linux-kernel/drivers/video/Makefile</p>
<pre class="brush: cpp; title: ; notranslate">
obj-$(CONFIG_FB_TFT)    += fbtft/
</pre>
<p>Then I edited the file ~/edison/poky/linux-kernel/drivers/video/fbtft/fbtft_device.c</p>
<p>I added the header</p>
<pre class="brush: cpp; title: ; notranslate">
#include &lt;linux/spi/intel_mid_ssp_spi.h&gt;
</pre>
<p>Then I added the description of my display into the list of displays.<br />
Where &#8220;reset&#8221; uses the number 49 of the GP49 pin, &#8220;dc&#8221; is GP15.<br />
The pin &#8220;led&#8221; is GP14 and I use it in my custom initialization code in the file fb_ssd1322.c</p>
<p>It seems that the chip SSD1322 supports speeds only up to 12.5MHz, but the Edison must be able to support speeds up to 25MHz.</p>
<pre class="brush: cpp; title: ; notranslate">
{
   .name = &quot;er_oled028&quot;,
   .spi = &amp;(struct spi_board_info) {
       .modalias = &quot;fb_ssd1322&quot;,
       .max_speed_hz = 12500000,
       .mode = SPI_MODE_3,
       .bus_num = 5,
       .chip_select = 0,
       .controller_data = &amp;(struct intel_mid_ssp_spi_chip) {
           .burst_size = DFLT_FIFO_BURST_SIZE,
           .timeout = DFLT_TIMEOUT_VAL,
           .dma_enabled = true,
        },
        .platform_data = &amp;(struct fbtft_platform_data) {
           .display = {
               .buswidth = 8,
               .backlight = 0,
               .width = 256,
               .height = 64,
           },
           .gpios = (const struct fbtft_gpio []) {
               { &quot;reset&quot;, 49 },
               { &quot;dc&quot;, 15},
               { &quot;led&quot;, 14},
               {},
           },
       }
   }
},
</pre>
<p>I modified the file ~/edison/poky/linux-kernel/drivers/video/fbtft/fbtft-core.c</p>
<pre class="brush: diff; title: ; notranslate">
--- fbtft-core.c	2015-03-05 02:54:01.000000000 -0800
+++ fbtft-core.c.new	2016-05-30 19:19:21.000000000 -0700
@@ -863,7 +863,7 @@
 	if (txbuflen &gt; 0) {
 		if (dma) {
 			dev-&gt;coherent_dma_mask = ~0;
-			txbuf = dmam_alloc_coherent(dev, txbuflen, &amp;par-&gt;txbuf.dma, GFP_DMA);
+			txbuf = devm_kzalloc(par-&gt;info-&gt;device, txbuflen, GFP_DMA | GFP_ATOMIC);
 		} else {
 			txbuf = devm_kzalloc(par-&gt;info-&gt;device, txbuflen, GFP_KERNEL);
 		}
</pre>
<p>I added the lines to the file ~/edison/poky/meta-intel-edison/meta-intel-edison-bsp/conf/machine/edison.conf to autoload the fbtft module.<br />
Where busnum=5 is the SPI bus number on the Edison.,<br />
name=er_oled028 defines my oled display from fbtft_device.c,<br />
debug=7 shows more debugging information. It&#8217;s optional.</p>
<pre class="brush: cpp; title: ; notranslate">
KERNEL_MODULE_AUTOLOAD += &quot;fbtft_device&quot;
module_conf_fbtft_device = &quot;options fbtft_device name=er_oled028 busnum=5 debug=7&quot;
KERNEL_MODULE_PROBECONF += &quot;fbtft_device&quot;
</pre>
<p>Then I recompiled the kernel and flashed my Edison.</p>
<p>I compiled mplayer on the Edison and played a sample video. I couldn&#8217;t find a video with the same dimensions as my display, but it should work in the full-screen mode too.</p>
<pre class="brush: bash; title: ; notranslate">
/usr/local/bin/mplayer -vo fbdev ultra.mp4
</pre>
		
		<!-- Begin Video.js Responsive Wrapper -->
		<div style='max-width:854px'>
			<div class='video-wrapper' style='padding-bottom:56.206088992974%;'>
				
	<!-- Begin Video.js -->
	<video id="example_video_id_1268919609" class="video-js vjs-default-skin" width="854" height="480" poster="http://hobby.farit.ru/wp-content/uploads/2016/05/video_playing.png" controls preload="none" data-setup='[]'>
		<source src="http://hobby.farit.ru/wp-content/uploads/2016/05/oled_video.mp4" type='video/mp4' />
		
		
	</video>
	<!-- End Video.js -->

			</div>
		</div>
		<!-- End Video.js Responsive Wrapper -->
		
<h5>Downloads</h5>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2016/05/kernel_edison_fbtft.tar.gz">Linux kernel for Edison with SPI DMA fixes and fbtft framebuffer</a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/linux-framebuffer-fbtft-with-dma-for-intel-edison/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
<enclosure url="http://hobby.farit.ru/wp-content/uploads/2016/05/oled_video.mp4" length="6725550" type="video/mp4" />
		</item>
		<item>
		<title>Framebuffer fbtft Installation on Intel Edison for OLED Display SSD1322</title>
		<link>https://hobby.farit.ru/framebuffer-fbtft-installation-intel-edison-oled-display-ssd1322/</link>
		<comments>https://hobby.farit.ru/framebuffer-fbtft-installation-intel-edison-oled-display-ssd1322/#comments</comments>
		<pubDate>Mon, 11 Apr 2016 03:59:14 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[display]]></category>
		<category><![CDATA[fbtft]]></category>
		<category><![CDATA[intel edison]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=102</guid>
		<description><![CDATA[Update: the version with working SPI DMA The Intel Edison doesn&#8217;t have a video interface; so, you connect an OLED or TFT LCD display via the SPI interface using the Linux kernel video module framebuffer. I used fbtft &#8211; Linux Framebuffer drivers for small TFT LCD display modules by Noralf Tronnes. My display is 2.8&#8243; [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2><span style="color: #ff0000;">Update: <a style="color: #ff0000;" href="http://hobby.farit.ru/linux-framebuffer-fbtft-with-dma-for-intel-edison/">the version with working SPI DMA</a></span></h2>
<p>The Intel Edison doesn&#8217;t have a video interface; so, you connect an OLED or TFT LCD display via the SPI interface using the Linux kernel video module framebuffer.</p>
<p>I used fbtft &#8211; <a href="https://github.com/notro/fbtft" target="_blank">Linux Framebuffer drivers for small TFT LCD display modules</a> by Noralf Tronnes.</p>
<p>My display is <a href="http://www.buydisplay.com/default/2-8-inch-oled-display-256x64-graphic-module-ssd1322-yellow-on-black" target="_blank">2.8&#8243; OLED Display 256&#215;64 Graphic Module SSD1322</a>.</p>
<p>After reading the documentation for the display, I connected it to the Edison via the 4-wire SPI interface, which requires an additional GPIO pin for the D/C signal (Write Data/Write Command). The 3-wire SPI interface uses the 9 bit SPI interface and should be avoided.</p>
<p>I compared the timing diagrams for the SSD1322 chip with <a href="http://dlnware.com/theory/SPI-Transfer-Modes" target="_blank">the SPI Transfer Modes</a> and found that it uses the SPI Mode 3.</p>
<h5>Schematic</h5>
<p>Here is the schematic of my SSD1322 shield for the Intel Edison.</p>
<figure id="attachment_104" style="width: 854px;" class="wp-caption aligncenter"><a href="http://hobby.farit.ru/wp-content/uploads/2016/04/display_schematic.png"><img class="size-medium wp-image-104" src="http://hobby.farit.ru/wp-content/uploads/2016/04/display_schematic-854x604.png" alt="SSD1322 Shield for Intel Edison" width="854" height="604" /></a><figcaption class="wp-caption-text">The SSD1322 Display Shield for the Intel Edison</figcaption></figure>
<h5>Connections between the Edison and the Display:</h5>
<p>GP49 -&gt; RES(Reset Signal Input)<br />
GP15 -&gt; D/C(Data/Command Control)<br />
GP110_SPI_2_FS0(CS0) -&gt; CS(Chip Select Input)<br />
GP109_SPI_2_CLK -&gt; D0/SCLK(Serial Clock)<br />
GP115_SPI_2_TXD -&gt; D1/SDIN(Serial Data Input)<br />
I also connected GP14 to the SHDN pin of the +12V voltage regulator.</p>
<h5>fbtft download and configuration</h5>
<p>The latest code from the <a href="https://github.com/notro/fbtft" target="_blank">notro/fbtft</a> repository didn&#8217;t work for me.<br />
I used the older code <a href="https://github.com/presslab-us/fbtft" target="_blank">presslab-us/fbtft</a>, which also included support for my chip SSD1322.</p>
<p>I prepared the Edison directory with the Linux sources as described in my article <a href="http://hobby.farit.ru/building-yocto-linux-for-intel-edison/" target="_blank">Building Yocto Linux for Intel Edison</a>.</p>
<p>After I compiled the Edison image for the first time, I copied these two hidden directories into another folder (I placed the Edison sources into the &#8220;edison&#8221; directory in my home directory); so, I can return the sources to the original state by copying these hidden directories back at any moment.</p>
<pre class="brush: plain; title: ; notranslate">
~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/.git
~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/.meta
</pre>
<p>I created the directory &#8220;fbtft&#8221; and copied all fbtft files there.</p>
<pre class="brush: bash; title: ; notranslate">
mkdir ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/fbtft
</pre>
<p>I added the line in ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/Kconfig before the line &#8220;endmenu&#8221;.</p>
<pre class="brush: bash; title: ; notranslate">
source &quot;drivers/video/fbtft/Kconfig&quot;
</pre>
<p>I added the line in ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/Makefile</p>
<pre class="brush: cpp; title: ; notranslate">
obj-$(CONFIG_FB_TFT)    += fbtft/
</pre>
<p>Then I edited the file ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/fbtft/fbtft_device.c</p>
<p>I added the header</p>
<pre class="brush: cpp; title: ; notranslate">
#include &lt;linux/spi/intel_mid_ssp_spi.h&gt;
</pre>
<p>Then I added the description of my display into the list of displays.<br />
Where &#8220;reset&#8221; uses the number 49 of the GP49 pin, &#8220;dc&#8221; is GP15.<br />
The pin &#8220;led&#8221; is GP14 and I use it in my custom initialization code in the file fb_ssd1322.c</p>
<p>DMA has to be disabled because the DMA code in intel_mid_ssp_spi.h is broken.<br />
If you enable it, you can see errors like this: &#8220;intel_mid_ssp_spi_unified 0000:00:07.1: ERROR : DMA buffers already mapped&#8221;</p>
<pre class="brush: cpp; title: ; notranslate">
{
   .name = &quot;er_oled028&quot;,
   .spi = &amp;(struct spi_board_info) {
       .modalias = &quot;fb_ssd1322&quot;,
       .max_speed_hz = 12000000,
       .mode = SPI_MODE_3,
       .bus_num = 5,
       .chip_select = 0,
       .controller_data = &amp;(struct intel_mid_ssp_spi_chip) {
           .burst_size = DFLT_FIFO_BURST_SIZE,
           .timeout = DFLT_TIMEOUT_VAL,
           .dma_enabled = false,
        },
        .platform_data = &amp;(struct fbtft_platform_data) {
           .display = {
               .buswidth = 8,
               .backlight = 0,
               .width = 256,
               .height = 64,
           },
           .gpios = (const struct fbtft_gpio []) {
               { &quot;reset&quot;, 49 },
               { &quot;dc&quot;, 15},
               { &quot;led&quot;, 14},
               {},
           },
       }
   }
},
</pre>
<p>I wrote my custom initialization code for the display ER_OLED028 in fb_ssd1322.c following the documentation for my display.</p>
<pre class="brush: cpp; title: ; notranslate">
static int init_display(struct fbtft_par *par)
{
   fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, &quot;%s()\n&quot;, __func__);

   //reset the chip
   mdelay(5);
   fbtft_par_dbg(DEBUG_RESET, par, &quot;%s()\n&quot;, __func__);
   gpio_set_value(par-&gt;gpio.reset, 0);
   udelay(200);
   gpio_set_value(par-&gt;gpio.reset, 1);
   udelay(200);

   write_reg(par, 0xfd, 0x12); /* Unlock OLED driver IC */
   write_reg(par, 0xae); /* Display Off */
   write_reg(par, 0xb3, 0x91); /* Display divide clockratio/frequency */
   write_reg(par, 0xca, 0x3f); /* Multiplex ratio, 1/64, 64 COMS enabled */
   write_reg(par, 0xa2, 0x00); /* Set offset, the display map starting line is COM0 */
   write_reg(par, 0xa1, 0x00); /* Set start line position */
   write_reg(par, 0xa0, 0x14, 0x11); /* Set remap, horiz address increment, disable colum address remap, */
				                      /*  enable nibble remap, scan from com[N-1] to COM0, disable COM split odd even */
   write_reg(par, 0xb5, 0x00); /* Set GPIO */
   write_reg(par, 0xab, 0x01); /* Select internal VDD */
   write_reg(par, 0xb4, 0xa0, 0xfd); /* Display enhancement A, external VSL, enhanced low GS display quality */
   write_reg(par, 0xc1, 0xff); /* Contrast current, 256 steps, default is 0x7F */
   write_reg(par, 0xc7, 0x0f); /* Master contrast current, 16 steps, default is 0x0F */
   write_reg(par, 0xb1, 0xe2); /* Phase Length */
   write_reg(par, 0xd1, 0x82, 0x20); /* Display enhancement B */
   write_reg(par, 0xbb, 0x1f); /* Pre-charge voltage */
   write_reg(par, 0xb6, 0x08); /* Set Second Pre-Charge Period */
   write_reg(par, 0xbe, 0x07); /* Set VCOMH */
   write_reg(par, 0xa6); /* Normal display */

   //enable VCC
   gpio_set_value(par-&gt;gpio.led[0], 1);
   mdelay(100);
  
   write_reg(par, 0xaf); /* Display ON */

   return 0;
}
</pre>
<p>After all modifications, I created a patch file.<br />
While committing to git, I added the comment line &#8220;fbtft_ssd1322&#8243;.</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video
git add .
git commit
git format-patch -1
</pre>
<p>It created the patch file &#8220;0001-fbtft_ssd1322.patch&#8221;. I renamed it to &#8220;fbtft_ssd1322.patch&#8221; and copied it to:</p>
<pre class="brush: plain; title: ; notranslate">
~edison/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/
</pre>
<p>I created the kernel configuration file fbtft.cfg in the same directory &#8220;recipes-kernel/linux/files/&#8221;:</p>
<pre class="brush: cpp; title: ; notranslate">
CONFIG_FRAMEBUFFER_CONSOLE=m
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=m
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_IMAGEBLIT=y
CONFIG_FB_SYS_FOPS=y
CONFIG_FB_DEFERRED_IO=y
CONFIG_FB_BACKLIGHT=y
CONFIG_FB_TFT=m
# CONFIG_FB_TFT_GU39XX is not set
# CONFIG_FB_TFT_HX8340BN is not set
# CONFIG_FB_TFT_HX8347D is not set
# CONFIG_FB_TFT_ILI9320 is not set
# CONFIG_FB_TFT_ILI9325 is not set
# CONFIG_FB_TFT_ILI9341 is not set
# CONFIG_FB_TFT_PCD8544 is not set
# CONFIG_FB_TFT_SSD1289 is not set
# CONFIG_FB_TFT_SSD1351 is not set
CONFIG_FB_TFT_SSD1322=m
# CONFIG_FB_TFT_ST7735R is not set
# CONFIG_FB_FLEX is not set
CONFIG_FB_TFT_FBTFT_DEVICE=m
# CONFIG_TOUCHSCREEN_ADS7846_DEVICE is not set
</pre>
<p>I commented the line in the file board.c for the device ads7955 that occupies the same SPI busnum=5, cs=0 that we use for the display.<br />
Then I ran the same git sequence to generate the patch file &#8220;platform_ssd1322.patch&#8221; and copied it into the same &#8220;recipes-kernel/linux/files&#8221; directory.</p>
<pre class="brush: diff; title: ; notranslate">
From c83f14b37bc2dcfa97b36214f02b15ddaad51a47 Mon Sep 17 00:00:00 2001
From: Farit &lt;farit@example.com&gt;
Date: Sat, 9 Apr 2016 22:02:19 -0700
Subject: [PATCH] platform_ssd1322

---
 arch/x86/platform/intel-mid/board.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/platform/intel-mid/board.c b/arch/x86/platform/intel-mid/board.c
index eb3d8a4..fca6440 100644
--- a/arch/x86/platform/intel-mid/board.c
+++ b/arch/x86/platform/intel-mid/board.c
@@ -111,7 +111,7 @@ struct devs_id __initconst device_ids[] = {
 
 	/* SPI devices */
 	{&quot;spidev&quot;, SFI_DEV_TYPE_SPI, 0, &amp;spidev_platform_data, NULL},
-	{&quot;ads7955&quot;, SFI_DEV_TYPE_SPI, 0, &amp;ads7955_platform_data, NULL},
+//	{&quot;ads7955&quot;, SFI_DEV_TYPE_SPI, 0, &amp;ads7955_platform_data, NULL},
 	{&quot;bma023&quot;, SFI_DEV_TYPE_I2C, 1, &amp;no_platform_data, NULL},
 	{&quot;pmic_gpio&quot;, SFI_DEV_TYPE_SPI, 1, &amp;pmic_gpio_platform_data, NULL},
 	{&quot;pmic_gpio&quot;, SFI_DEV_TYPE_IPC, 1, &amp;pmic_gpio_platform_data,
-- 
1.9.1
</pre>
<p>When all patch and configuration files were ready, I added links to them into the file &#8220;~/edison/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/linux-yocto_3.10.bbappend&#8221;.</p>
<pre class="brush: cpp; title: ; notranslate">
SRC_URI += &quot;file://defconfig&quot;
SRC_URI += &quot;file://upstream_to_edison.patch&quot;

SRC_URI += &quot;file://fbtft.cfg&quot;
SRC_URI += &quot;file://fbtft_ssd1322.patch&quot;
SRC_URI += &quot;file://platform_ssd1322.patch&quot;
</pre>
<h5>fbtft Module Compilation</h5>
<p>Then I recompiled the Linux image and installed it.</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image
~/edison/edison-src/meta-intel-edison/utils/flash/postBuild.sh
~/edison/edison-src/build/toFlash/flashall.sh
</pre>
<h5>Loading and Testing the Framebuffer Module</h5>
<p>I loaded the fbtft module supplying the name of the display and the SPI bus number (it&#8217;s 5 on the Edison).</p>
<pre class="brush: bash; title: ; notranslate">
modprobe fbtft_device name=er_oled028 busnum=5
</pre>
<p>Sometimes, it&#8217;s not loaded. I need to investigate the problems.<br />
When it loads, the &#8220;dmesg&#8221; command shows:</p>
<pre class="brush: plain; title: ; notranslate">
[   32.254181] fbtft_device:  GPIOS used by 'er_oled028':
[   32.254205] fbtft_device:    'reset' = GPIO49
[   32.254221] fbtft_device:    'dc' = GPIO15
[   32.254234] fbtft_device:    'led' = GPIO14
[   32.254246] fbtft_device:  SPI devices registered:
[   32.254264] fbtft_device:      spidev spi5.1 25000kHz 8 bits mode=0x00
[   32.254281] fbtft_device:      fb_ssd1322 spi5.0 12500kHz 8 bits mode=0x03
[   32.394493] graphics fb0: fb_ssd1322 frame buffer, 256x64, 32 KiB video memory, 8 KiB buffer memory, fps=20, spi5.0 at 12 MHz
</pre>
<p>Then I can test the module by sending random values to the framebuffer socket &#8220;/dev/fb0&#8243;.</p>
<pre class="brush: bash; title: ; notranslate">
cat /dev/urandom &gt; /dev/fb0
</pre>
<p>Here is the result.</p>
<figure id="attachment_117" style="width: 854px;" class="wp-caption aligncenter"><a href="http://hobby.farit.ru/wp-content/uploads/2016/04/display_urandom.png"><img class="size-medium wp-image-117" src="http://hobby.farit.ru/wp-content/uploads/2016/04/display_urandom-854x305.png" alt="Sending Random Data to the Framebuffer" width="854" height="305" /></a><figcaption class="wp-caption-text">Sending Random Data to the Framebuffer</figcaption></figure>
<p>I can also load the fbcon module to display the console on the display.</p>
<pre class="brush: bash; title: ; notranslate">
modprobe fbcon
</pre>
<figure id="attachment_118" style="width: 854px;" class="wp-caption aligncenter"><a href="http://hobby.farit.ru/wp-content/uploads/2016/04/fbcon.png"><img class="size-medium wp-image-118" src="http://hobby.farit.ru/wp-content/uploads/2016/04/fbcon-854x291.png" alt="Edison Console via the Framebuffer" width="854" height="291" /></a><figcaption class="wp-caption-text">Edison Console via the Framebuffer</figcaption></figure>
<h5>Improvements</h5>
<p>There are patches on the Edison forum that may help to fix the DMA issue. Otherwise, the display will be relatively slow.<br />
Check this link <a href="https://communities.intel.com/docs/DOC-24944" target="_blank">stewart maguire&#8217;s patches for DMA and FBTFT support</a>.<br />
And this <a href="https://communities.intel.com/thread/76768?start=0&amp;tstart=0" target="_blank">Intel MID SSP SPI driver getting stuck in kernel space</a>.</p>
<h5>Downloads</h5>
<p><a href="http://hobby.farit.ru/wp-content/uploads/2016/04/ssd1322patch.tar.gz">Configuration and patch files for fbtft on Intel Edison</a></p>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/framebuffer-fbtft-installation-intel-edison-oled-display-ssd1322/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Building Yocto Linux for Intel Edison</title>
		<link>https://hobby.farit.ru/building-yocto-linux-for-intel-edison/</link>
		<comments>https://hobby.farit.ru/building-yocto-linux-for-intel-edison/#comments</comments>
		<pubDate>Sat, 01 Aug 2015 04:39:11 +0000</pubDate>
		<dc:creator><![CDATA[Farit]]></dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[intel edison]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[yocto]]></category>

		<guid isPermaLink="false">http://hobby.farit.ru/?p=84</guid>
		<description><![CDATA[The Intel Edison board is shipped with the Yocto Linux distribution installed. Yocto allows an experienced developer to compile a small-size Linux image with only the selected packages. Then the image is flashed into the board. It&#8217;s not as convenient as Ubuntu as there are no pre-compiled packages that are easy to install. I wanted [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>The Intel Edison board is shipped with the <a href="https://www.yoctoproject.org/" target="_blank"><strong>Yocto</strong></a> Linux distribution installed. Yocto allows an experienced developer to compile a small-size Linux image with only the selected packages. Then the image is flashed into the board. It&#8217;s not as convenient as Ubuntu as there are no pre-compiled packages that are easy to install.</p>
<p>I wanted to add the program mc (Midnight Commander); so, I had to recompile the Yocto image. My computer OS is Linux Ubuntu 14.04. I experienced a few problems while compiling; so, I&#8217;ll describe them here.</p>
<p>The main document is <strong><a href="http://www.intel.com/support/edison/sb/CS-035278.htm" target="_blank">Board Support Package (BSP) User Guide</a></strong>. I followed it.</p>
<p>Installed the prerequisite packages with the following command:</p>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get install build-essential git diffstat gawk chrpath texinfo libtool \
gcc-multilib dfu-util u-boot-tools
</pre>
<p>Set up my git name and email:</p>
<pre class="brush: plain; title: ; notranslate">
git config --global user.name &quot;YOUR NAME&quot;
git config --global user.email &quot;YOUR EMAIL ADDRESS&quot;
</pre>
<p>Downloaded the package <strong>Linux source files</strong> from the <a href="https://software.intel.com/iot/hardware/edison/downloads" target="_blank"><strong>Intel Edison Software Downloads</strong></a> page. At the time of writing, it was in the section <strong>Intel Edison® Board Firmware Software Release 2.1</strong>.</p>
<p>I install everything related to the Intel Edison into the directory /home/farit/edison. The Linux sources consume 11G in that directory.</p>
<p>Decompressed the source package:</p>
<pre class="brush: plain; title: ; notranslate">
tar xzf edison-src-ww25.5-15.tgz  -C /home/farit/edison/
cd /home/farit/edison/edison-src/
</pre>
<p>Moved my download and build cache (also called sstate) directories from the default location under the build directory, using the &#8211;dl_dir and &#8211;sstate_dir options. To create download and sstate directory, used the mkdir command:</p>
<pre class="brush: plain; title: ; notranslate">
mkdir /home/farit/edison/bitbake_download_dir
mkdir /home/farit/edison/bitbake_sstate_dir
</pre>
<p>Used the setup.sh script to initialize the build environment for Intel Edison.</p>
<pre class="brush: plain; title: ; notranslate">
/home/farit/edison/edison-src/meta-intel-edison/setup.sh \
--dl_dir=/home/farit/edison/bitbake_download_dir \
--sstate_dir=/home/farit/edison/bitbake_sstate_dir \
--build_dir=/home/farit/edison/edison-src
</pre>
<p>After the setup, I ran these commands recommended by the setup script:</p>
<pre class="brush: plain; title: ; notranslate">
cd /home/farit/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image
</pre>
<p>The compilation took 5 or so hours. I should&#8217;ve used the SSD disk. But most files have been downloaded and cached now; so, it doesn&#8217;t take much time the second time.</p>
<p>Then I decided to compile in Midnight Commander. I checked the name of the package on the <a href="http://packages.yoctoproject.org/" target="_blank">Yocto recipes directory</a>. It was &#8220;mc&#8221;.<br />
I opened the file /home/farit/edison/edison-src/meta-intel-edison/meta-intel-edison-distro/recipes-core/images/edison-image.bb and added the lines at the bottom:</p>
<pre class="brush: plain; title: ; notranslate">
#Midnight Commander
IMAGE_INSTALL += &quot;mc&quot;
</pre>
<p>Then ran again:</p>
<pre class="brush: plain; title: ; notranslate">
cd /home/farit/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image
</pre>
<p>Then I created a new Linux image:</p>
<pre class="brush: plain; title: ; notranslate">
/home/farit/edison/edison-src/meta-intel-edison/utils/flash/postBuild.sh
/home/farit/edison/edison-src/build/toFlash/flashall.sh
</pre>
<p>At first, the command complained about the non-existing mkimage program, which is in the package u-boot-tools:</p>
<pre class="brush: plain; title: ; notranslate">
Error : ota_update.scr creation failed, mkimage tool not found
</pre>
<p>I added a symlink to mkimage and ran the postBuild.sh script again:</p>
<pre class="brush: plain; title: ; notranslate">
cd /home/farit/edison/edison-src
mkdir -p u-boot/tools
cd u-boot/tools
ln -s /usr/bin/mkimage mkimage
</pre>
<p>When there were no errors, I looked in the directory /home/farit/edison/edison-src/build/toFlash. It contained the same files as in the Intel&#8217;s firmware image.<br />
I ran the command flashall.sh in it as root. I added myself into the group &#8220;dialout&#8221;; so, I should be able to connect to a USB port as a regular user. I&#8217;ll check it again the next time.</p>
<figure id="attachment_87" style="width: 854px;" class="wp-caption alignnone"><a href="http://hobby.farit.ru/wp-content/uploads/2015/07/mc_edison.png"><img class="size-medium wp-image-87" src="http://hobby.farit.ru/wp-content/uploads/2015/07/mc_edison-854x463.png" alt="Midnight Commander on Intel Edison" width="854" height="463" /></a><figcaption class="wp-caption-text">Midnight Commander on Intel Edison</figcaption></figure>
<p>By default, the subshell (Ctrl-O) doesn&#8217;t work in Midnight Commander as it requires bash. Change your user shell from the default &#8220;/bin/sh&#8221; to &#8220;/bin/bash&#8221; in /etc/passwd.</p>
<p>When I want to quickly recompile just the kernel after changes, I run these commands:</p>
<pre class="brush: plain; title: ; notranslate">
cd /home/farit/edison/edison-src
source poky/oe-init-build-env
bitbake -f linux-yocto
</pre>
]]></content:encoded>
			<wfw:commentRss>https://hobby.farit.ru/building-yocto-linux-for-intel-edison/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
